aboutsummaryrefslogtreecommitdiff
path: root/stockton-render
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:21 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:21 +0100
commit2111c1248b08236a839dcf22036f92735bceb31c (patch)
tree9313da344b7134a913d1d917162e55b35fe1e74f /stockton-render
parent102e166b040030b590df83888a1d1a47d0130f10 (diff)
chore(all): style formatting and clippy fixes
Diffstat (limited to 'stockton-render')
-rw-r--r--stockton-render/src/culling.rs90
-rw-r--r--stockton-render/src/draw/buffer.rs373
-rw-r--r--stockton-render/src/draw/camera.rs339
-rw-r--r--stockton-render/src/draw/context.rs1273
-rw-r--r--stockton-render/src/draw/macros.rs10
-rw-r--r--stockton-render/src/draw/mod.rs4
-rw-r--r--stockton-render/src/draw/target.rs788
-rw-r--r--stockton-render/src/draw/texture/chunk.rs265
-rw-r--r--stockton-render/src/draw/texture/image.rs655
-rw-r--r--stockton-render/src/draw/texture/loader.rs286
-rw-r--r--stockton-render/src/draw/texture/mod.rs6
-rw-r--r--stockton-render/src/draw/texture/resolver.rs47
-rw-r--r--stockton-render/src/error.rs57
-rw-r--r--stockton-render/src/lib.rs67
-rw-r--r--stockton-render/src/types.rs2
15 files changed, 2280 insertions, 1982 deletions
diff --git a/stockton-render/src/culling.rs b/stockton-render/src/culling.rs
index 8240701..dbe5a1e 100644
--- a/stockton-render/src/culling.rs
+++ b/stockton-render/src/culling.rs
@@ -20,59 +20,61 @@ use stockton_levels::prelude::*;
use stockton_levels::traits::tree::{BSPNode, BSPNodeValue};
use stockton_types::Vector3;
-
/// Get the visible faces according to visdata and frustum culling
// TODO: Write this. For now, just render all faces
pub fn get_visible_faces<X: CoordSystem, T: MinBSPFeatures<X>>(pos: Vector3, file: &T) -> Vec<u32> {
- let vis_cluster = get_cluster_id(pos, file);
-
- if (vis_cluster & 0x80000000) != 0 { // Negative = Invalid camera position
- return vec![];
- }
+ let vis_cluster = get_cluster_id(pos, file);
+
+ if (vis_cluster & 0x80000000) != 0 {
+ // Negative = Invalid camera position
+ return vec![];
+ }
- let mut visible = Vec::with_capacity(file.faces_len() as usize);
- walk_bsp_tree(file.get_bsp_root(), vis_cluster, &mut visible, file);
+ let mut visible = Vec::with_capacity(file.faces_len() as usize);
+ walk_bsp_tree(file.get_bsp_root(), vis_cluster, &mut visible, file);
- return visible;
+ visible
}
-pub fn walk_bsp_tree<X: CoordSystem, T: MinBSPFeatures<X>>(node: &BSPNode, vis_cluster: u32, visible_faces: &mut Vec<u32>, file: &T) -> () {
- if let BSPNodeValue::Children(front, back) = &node.value {
- walk_bsp_tree(back, vis_cluster, visible_faces, file);
- walk_bsp_tree(front, vis_cluster, visible_faces, file);
- } else if let BSPNodeValue::Leaf(leaf) = &node.value {
- if (leaf.cluster_id & 0x80000000) != 0 { // Negative means invalid leaf
- return;
- } else if file.cluster_visible_from(vis_cluster, leaf.cluster_id) {
- for face_idx in leaf.faces_idx.iter() {
- // TODO: Culling or something
- visible_faces.push(*face_idx);
- }
- }
- }
+pub fn walk_bsp_tree<X: CoordSystem, T: MinBSPFeatures<X>>(
+ node: &BSPNode,
+ vis_cluster: u32,
+ visible_faces: &mut Vec<u32>,
+ file: &T,
+) {
+ if let BSPNodeValue::Children(front, back) = &node.value {
+ walk_bsp_tree(back, vis_cluster, visible_faces, file);
+ walk_bsp_tree(front, vis_cluster, visible_faces, file);
+ } else if let BSPNodeValue::Leaf(leaf) = &node.value {
+ if (leaf.cluster_id & 0x80000000) != 0 {
+ // Negative means invalid leaf
+ return;
+ } else if file.cluster_visible_from(vis_cluster, leaf.cluster_id) {
+ for face_idx in leaf.faces_idx.iter() {
+ // TODO: Culling or something
+ visible_faces.push(*face_idx);
+ }
+ }
+ }
}
-/// Get the viscluster pos lies in
+/// Get the viscluster pos lies in
fn get_cluster_id<X: CoordSystem, T: MinBSPFeatures<X>>(pos: Vector3, file: &T) -> u32 {
- let mut node = file.get_bsp_root();
- loop {
- if let BSPNodeValue::Children(front, back) = &node.value {
- let plane = file.get_plane(node.plane_idx);
- let dist = plane.normal.dot(&pos) - plane.dist;
+ let mut node = file.get_bsp_root();
+ while let BSPNodeValue::Children(front, back) = &node.value {
+ let plane = file.get_plane(node.plane_idx);
+ let dist = plane.normal.dot(&pos) - plane.dist;
- if dist >= 0.0 {
- node = front;
- } else {
- node = back;
- }
- } else {
- break;
- }
- }
+ if dist >= 0.0 {
+ node = front;
+ } else {
+ node = back;
+ }
+ }
- if let BSPNodeValue::Leaf(leaf) = &node.value {
- leaf.cluster_id
- } else {
- panic!("should have had a leaf but didn't");
- }
-} \ No newline at end of file
+ if let BSPNodeValue::Leaf(leaf) = &node.value {
+ leaf.cluster_id
+ } else {
+ panic!("should have had a leaf but didn't");
+ }
+}
diff --git a/stockton-render/src/draw/buffer.rs b/stockton-render/src/draw/buffer.rs
index cbe56f4..9b3e726 100644
--- a/stockton-render/src/draw/buffer.rs
+++ b/stockton-render/src/draw/buffer.rs
@@ -1,4 +1,4 @@
-// Copyright (C) 2019 Oscar Shrimpton
+// 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
@@ -13,17 +13,17 @@
// 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 core::mem::{size_of, ManuallyDrop};
+use std::convert::TryInto;
use std::iter::once;
use std::ops::{Index, IndexMut};
-use std::convert::TryInto;
-use core::mem::{ManuallyDrop, size_of};
use hal::prelude::*;
use hal::{
- MemoryTypeId,
- buffer::Usage,
- memory::{Properties, Segment},
- queue::Submission
+ buffer::Usage,
+ memory::{Properties, Segment},
+ queue::Submission,
+ MemoryTypeId,
};
use crate::error::CreationError;
@@ -31,193 +31,224 @@ use crate::types::*;
/// Create a buffer of the given specifications, allocating more device memory.
// TODO: Use a different memory allocator?
-pub(crate) fn create_buffer(device: &mut Device,
- adapter: &Adapter,
- usage: Usage,
- properties: Properties,
- size: u64) -> Result<(Buffer, Memory), CreationError> {
- let mut buffer = unsafe { device
- .create_buffer(size, usage) }
- .map_err(|e| CreationError::BufferError (e))?;
-
- let requirements = unsafe { device.get_buffer_requirements(&buffer) };
- let memory_type_id = adapter.physical_device
- .memory_properties().memory_types
- .iter().enumerate()
- .find(|&(id, memory_type)| {
- requirements.type_mask & (1 << id) != 0 && memory_type.properties.contains(properties)
- })
- .map(|(id, _)| MemoryTypeId(id))
- .ok_or(CreationError::BufferNoMemory)?;
-
- let memory = unsafe {device
- .allocate_memory(memory_type_id, requirements.size) }
- .map_err(|_| CreationError::OutOfMemoryError)?;
-
- unsafe { device
- .bind_buffer_memory(&memory, 0, &mut buffer) }
- .map_err(|_| CreationError::BufferNoMemory)?;
-
- Ok((buffer, memory))
+pub(crate) fn create_buffer(
+ device: &mut Device,
+ adapter: &Adapter,
+ usage: Usage,
+ properties: Properties,
+ size: u64,
+) -> Result<(Buffer, Memory), CreationError> {
+ let mut buffer =
+ unsafe { device.create_buffer(size, usage) }.map_err(CreationError::BufferError)?;
+
+ let requirements = unsafe { device.get_buffer_requirements(&buffer) };
+ let memory_type_id = adapter
+ .physical_device
+ .memory_properties()
+ .memory_types
+ .iter()
+ .enumerate()
+ .find(|&(id, memory_type)| {
+ requirements.type_mask & (1 << id) != 0 && memory_type.properties.contains(properties)
+ })
+ .map(|(id, _)| MemoryTypeId(id))
+ .ok_or(CreationError::BufferNoMemory)?;
+
+ let memory = unsafe { device.allocate_memory(memory_type_id, requirements.size) }
+ .map_err(|_| CreationError::OutOfMemoryError)?;
+
+ unsafe { device.bind_buffer_memory(&memory, 0, &mut buffer) }
+ .map_err(|_| CreationError::BufferNoMemory)?;
+
+ Ok((buffer, memory))
}
/// A buffer that can be modified by the CPU
pub trait ModifiableBuffer: IndexMut<usize> {
- /// Get a handle to the underlying GPU buffer
- fn get_buffer<'a>(&'a mut self) -> &'a Buffer;
-
- /// Commit all changes to GPU memory, returning a handle to the GPU buffer
- fn commit<'a>(&'a mut self, device: &Device,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool) -> &'a Buffer;
+ /// Get a handle to the underlying GPU buffer
+ fn get_buffer(&mut self) -> &Buffer;
+
+ /// Commit all changes to GPU memory, returning a handle to the GPU buffer
+ fn commit<'a>(
+ &'a mut self,
+ device: &Device,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ ) -> &'a Buffer;
}
/// A GPU buffer that is written to using a staging buffer
pub struct StagedBuffer<'a, T: Sized> {
- /// CPU-visible buffer
- staged_buffer: ManuallyDrop<Buffer>,
+ /// CPU-visible buffer
+ staged_buffer: ManuallyDrop<Buffer>,
- /// CPU-visible memory
- staged_memory: ManuallyDrop<Memory>,
+ /// CPU-visible memory
+ staged_memory: ManuallyDrop<Memory>,
- /// GPU Buffer
- buffer: ManuallyDrop<Buffer>,
+ /// GPU Buffer
+ buffer: ManuallyDrop<Buffer>,
- /// GPU Memory
- memory: ManuallyDrop<Memory>,
+ /// GPU Memory
+ memory: ManuallyDrop<Memory>,
- /// Where staged buffer is mapped in CPU memory
- staged_mapped_memory: &'a mut [T],
+ /// Where staged buffer is mapped in CPU memory
+ staged_mapped_memory: &'a mut [T],
- /// If staged memory has been changed since last `commit`
- staged_is_dirty: bool,
+ /// If staged memory has been changed since last `commit`
+ staged_is_dirty: bool,
- /// The highest index in the buffer that's been written to.
- pub highest_used: usize
+ /// The highest index in the buffer that's been written to.
+ pub highest_used: usize,
}
-
impl<'a, T: Sized> StagedBuffer<'a, T> {
- /// size is the size in T
- pub fn new(device: &mut Device, adapter: &Adapter, usage: Usage, size: u64) -> Result<Self, CreationError> {
- // Convert size to bytes
- let size_bytes = size * size_of::<T>() as u64;
-
- // Get CPU-visible buffer
- let (staged_buffer, staged_memory) = create_buffer(device, adapter, Usage::TRANSFER_SRC, Properties::CPU_VISIBLE, size_bytes)?;
-
- // Get GPU Buffer
- let (buffer, memory) = create_buffer(device, adapter, Usage::TRANSFER_DST | usage, Properties::DEVICE_LOCAL, size_bytes)?;
-
- // Map it somewhere and get a slice to that memory
- let staged_mapped_memory = unsafe {
- let ptr = device.map_memory(&staged_memory, Segment::ALL).unwrap(); // TODO
-
- std::slice::from_raw_parts_mut(ptr as *mut T, size.try_into().unwrap())
- };
-
- Ok(StagedBuffer {
- staged_buffer: ManuallyDrop::new(staged_buffer),
- staged_memory: ManuallyDrop::new(staged_memory),
- buffer: ManuallyDrop::new(buffer),
- memory: ManuallyDrop::new(memory),
- staged_mapped_memory,
- staged_is_dirty: false,
- highest_used: 0
- })
- }
-
- /// Call this before dropping
- 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));
- };
- }
+ /// size is the size in T
+ pub fn new(
+ device: &mut Device,
+ adapter: &Adapter,
+ usage: Usage,
+ size: u64,
+ ) -> Result<Self, CreationError> {
+ // Convert size to bytes
+ let size_bytes = size * size_of::<T>() as u64;
+
+ // Get CPU-visible buffer
+ let (staged_buffer, staged_memory) = create_buffer(
+ device,
+ adapter,
+ Usage::TRANSFER_SRC,
+ Properties::CPU_VISIBLE,
+ size_bytes,
+ )?;
+
+ // Get GPU Buffer
+ let (buffer, memory) = create_buffer(
+ device,
+ adapter,
+ Usage::TRANSFER_DST | usage,
+ Properties::DEVICE_LOCAL,
+ size_bytes,
+ )?;
+
+ // Map it somewhere and get a slice to that memory
+ let staged_mapped_memory = unsafe {
+ let ptr = device.map_memory(&staged_memory, Segment::ALL).unwrap(); // TODO
+
+ std::slice::from_raw_parts_mut(ptr as *mut T, size.try_into().unwrap())
+ };
+
+ Ok(StagedBuffer {
+ staged_buffer: ManuallyDrop::new(staged_buffer),
+ staged_memory: ManuallyDrop::new(staged_memory),
+ buffer: ManuallyDrop::new(buffer),
+ memory: ManuallyDrop::new(memory),
+ staged_mapped_memory,
+ staged_is_dirty: false,
+ highest_used: 0,
+ })
+ }
+
+ /// Call this before dropping
+ 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 get_buffer<'b>(&'b mut self) -> &'b Buffer {
- &self.buffer
- }
-
- fn commit<'b>(&'b mut self, device: &Device,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool) -> &'b Buffer {
- // Only commit if there's changes to commit.
- if self.staged_is_dirty {
-
- // Flush mapped memory to ensure the staged buffer is filled
- unsafe {
- use std::ops::Deref;
- device.flush_mapped_memory_ranges(once((self.staged_memory.deref(), Segment::ALL))).unwrap();
- }
-
- // 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: ((self.highest_used + 1) * size_of::<T>()) as u64
- }
- ]);
- 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();
-
- // Destroy temporary resources
- device.destroy_fence(copy_finished);
- command_pool.free(once(buf));
- }
-
- self.staged_is_dirty = false;
- }
-
- &self.buffer
- }
+impl<'a, T: Sized> ModifiableBuffer for StagedBuffer<'a, T> {
+ fn get_buffer(&mut self) -> &Buffer {
+ &self.buffer
+ }
+
+ fn commit<'b>(
+ &'b mut self,
+ device: &Device,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ ) -> &'b Buffer {
+ // Only commit if there's changes to commit.
+ if self.staged_is_dirty {
+ // Flush mapped memory to ensure the staged buffer is filled
+ unsafe {
+ use std::ops::Deref;
+ device
+ .flush_mapped_memory_ranges(once((self.staged_memory.deref(), Segment::ALL)))
+ .unwrap();
+ }
+
+ // Copy from staged to buffer
+ let buf = unsafe {
+ use hal::command::{BufferCopy, CommandBufferFlags};
+ // 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: ((self.highest_used + 1) * size_of::<T>()) as u64,
+ }],
+ );
+ 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();
+
+ // Destroy temporary resources
+ device.destroy_fence(copy_finished);
+ command_pool.free(once(buf));
+ }
+
+ self.staged_is_dirty = false;
+ }
+
+ &self.buffer
+ }
}
impl<'a, T: Sized> Index<usize> for StagedBuffer<'a, T> {
- type Output = T;
+ type Output = T;
- fn index(&self, index: usize) -> &Self::Output {
- &self.staged_mapped_memory[index]
- }
+ fn index(&self, index: usize) -> &Self::Output {
+ &self.staged_mapped_memory[index]
+ }
}
impl<'a, T: Sized> IndexMut<usize> for StagedBuffer<'a, T> {
- fn index_mut(&mut self, index: usize) -> &mut Self::Output {
- self.staged_is_dirty = true;
- if index > self.highest_used {
- self.highest_used = index;
- }
- &mut self.staged_mapped_memory[index]
- }
-} \ No newline at end of file
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ self.staged_is_dirty = true;
+ if index > self.highest_used {
+ self.highest_used = index;
+ }
+ &mut self.staged_mapped_memory[index]
+ }
+}
diff --git a/stockton-render/src/draw/camera.rs b/stockton-render/src/draw/camera.rs
index 5737928..cb465fd 100644
--- a/stockton-render/src/draw/camera.rs
+++ b/stockton-render/src/draw/camera.rs
@@ -15,10 +15,10 @@
//! Things related to converting 3D world space to 2D screen space
-use stockton_types::{Vector3, Matrix4};
+use stockton_types::{Matrix4, Vector3};
-use std::f32::consts::PI;
use na::{look_at_lh, perspective_lh_zo, Mat4, Vec4};
+use std::f32::consts::PI;
/// 90 degrees in radians
const R89: f32 = (PI / 180.0) * 89.0;
@@ -30,201 +30,204 @@ const R90: f32 = PI / 2.0;
const R180: f32 = PI;
fn euler_to_direction(euler: &Vector3) -> Vector3 {
- let pitch = euler.x;
- let yaw = euler.y;
- let _roll = euler.z; // TODO: Support camera roll
-
- Vector3::new(
- yaw.sin() * pitch.cos(),
- pitch.sin(),
- yaw.cos() * pitch.cos()
- )
+ let pitch = euler.x;
+ let yaw = euler.y;
+ let _roll = euler.z; // TODO: Support camera roll
+
+ Vector3::new(
+ yaw.sin() * pitch.cos(),
+ pitch.sin(),
+ yaw.cos() * pitch.cos(),
+ )
}
pub struct CameraSettings {
- /// Position of the camera (world units)
- pub position: Vector3,
+ /// Position of the camera (world units)
+ pub position: Vector3,
- /// Rotation of the camera (euler angles in radians)
- pub rotation: Vector3,
+ /// Rotation of the camera (euler angles in radians)
+ pub rotation: Vector3,
- /// The up direction (normalized)
- pub up: Vector3,
+ /// The up direction (normalized)
+ pub up: Vector3,
- /// FOV (radians)
- pub fov: f32,
+ /// FOV (radians)
+ pub fov: f32,
- /// Near clipping plane (world units)
- pub near: f32,
+ /// Near clipping plane (world units)
+ pub near: f32,
- /// Far clipping plane (world units)
- pub far: f32,
+ /// Far clipping plane (world units)
+ pub far: f32,
}
/// Holds settings related to the projection of world space to screen space
/// Also holds maths for generating important matrices
pub struct WorkingCamera {
- /// Settings for the camera
- settings: CameraSettings,
+ /// Settings for the camera
+ settings: CameraSettings,
- /// Aspect ratio as a fraction
- aspect_ratio: f32,
+ /// Aspect ratio as a fraction
+ aspect_ratio: f32,
- /// Cached view projection matrix
- vp_matrix: Mat4,
+ /// Cached view projection matrix
+ vp_matrix: Mat4,
- /// If true, cached value needs updated
- is_dirty: bool
+ /// If true, cached value needs updated
+ is_dirty: bool,
}
impl WorkingCamera {
- /// Return a camera with default settings
- pub fn defaults(aspect_ratio: f32) -> WorkingCamera {
- WorkingCamera::with_settings(CameraSettings {
- position: Vector3::new(0.0, 0.0, 0.0),
- rotation: Vector3::new(0.0, R90, 0.0),
- up: Vector3::new(0.0, 1.0, 0.0),
- fov: f32::to_radians(90.0),
- near: 0.1,
- far: 1024.0,
- }, aspect_ratio)
- }
-
- /// Return a camera with the given settings
- pub fn with_settings(settings: CameraSettings, aspect_ratio: f32) -> WorkingCamera {
- WorkingCamera {
- aspect_ratio,
- settings,
- vp_matrix: Mat4::identity(),
- is_dirty: true
- }
- }
-
- /// Get the VP matrix, updating cache if needed
- pub fn get_matrix<'a>(&'a mut self) -> &'a Mat4 {
- // Update matrix if needed
- if self.is_dirty {
- self.vp_matrix = self.calc_vp_matrix();
- self.is_dirty = false;
- }
-
- // Return the matrix
- &self.vp_matrix
- }
-
- /// Returns a matrix that transforms from world space to screen space
- fn calc_vp_matrix(&self) -> Matrix4 {
- // Get look direction from euler angles
- let direction = euler_to_direction(&self.settings.rotation);
-
- // Converts world space to camera space
- let view_matrix = look_at_lh(
- &self.settings.position,
- &(direction + &self.settings.position),
- &self.settings.up
- );
-
- // Converts camera space to screen space
- let projection_matrix = {
- let mut temp = perspective_lh_zo(
- self.aspect_ratio,
- self.settings.fov,
- self.settings.near,
- self.settings.far
- );
-
- // Vulkan's co-ord system is different from OpenGLs
- temp[(1, 1)] *= -1.0;
-
- temp
- };
-
- // Chain them together into a single matrix
- projection_matrix * view_matrix
- }
-
- /// Update the aspect ratio
- pub fn update_aspect_ratio(&mut self, new: f32) {
- self.aspect_ratio = new;
- self.is_dirty = true;
- }
-
- /// Apply rotation of the camera
- /// `euler` should be euler angles in degrees
- pub fn rotate(&mut self, euler: Vector3) {
- // TODO
- self.settings.rotation += euler;
-
- // Clamp -pi/2 < pitch < pi/2
- if self.settings.rotation.x > R89 {
- self.settings.rotation.x = R89;
- } else if self.settings.rotation.x <= -R89 {
- self.settings.rotation.x = -R89;
- }
-
- // -pi < yaw <= pi
- if self.settings.rotation.y <= -R180 {
- self.settings.rotation.y = R180 - self.settings.rotation.y % -R180;
- } else if self.settings.rotation.y > 180.0 {
- self.settings.rotation.y = -R180 + self.settings.rotation.y % R180;
- }
-
- self.is_dirty = true;
- }
-
- /// Move the camera by `delta`, relative to the camera's rotation
- pub fn move_camera_relative(&mut self, delta: Vector3) {
- let rot_matrix = Mat4::from_euler_angles(
- -self.settings.rotation.x,
- self.settings.rotation.y,
- self.settings.rotation.z
- );
-
- let new = rot_matrix * Vec4::new(delta.x, delta.y, delta.z, 1.0);
- self.settings.position.x += new.x;
- self.settings.position.y += new.y;
- self.settings.position.z += new.z;
-
- self.is_dirty = true;
- }
-
- pub fn camera_pos(&self) -> Vector3 {
- self.settings.position
- }
+ /// Return a camera with default settings
+ pub fn defaults(aspect_ratio: f32) -> WorkingCamera {
+ WorkingCamera::with_settings(
+ CameraSettings {
+ position: Vector3::new(0.0, 0.0, 0.0),
+ rotation: Vector3::new(0.0, R90, 0.0),
+ up: Vector3::new(0.0, 1.0, 0.0),
+ fov: f32::to_radians(90.0),
+ near: 0.1,
+ far: 1024.0,
+ },
+ aspect_ratio,
+ )
+ }
+
+ /// Return a camera with the given settings
+ pub fn with_settings(settings: CameraSettings, aspect_ratio: f32) -> WorkingCamera {
+ WorkingCamera {
+ aspect_ratio,
+ settings,
+ vp_matrix: Mat4::identity(),
+ is_dirty: true,
+ }
+ }
+
+ /// Get the VP matrix, updating cache if needed
+ pub fn get_matrix(&mut self) -> &Mat4 {
+ // Update matrix if needed
+ if self.is_dirty {
+ self.vp_matrix = self.calc_vp_matrix();
+ self.is_dirty = false;
+ }
+
+ // Return the matrix
+ &self.vp_matrix
+ }
+
+ /// Returns a matrix that transforms from world space to screen space
+ fn calc_vp_matrix(&self) -> Matrix4 {
+ // Get look direction from euler angles
+ let direction = euler_to_direction(&self.settings.rotation);
+
+ // Converts world space to camera space
+ let view_matrix = look_at_lh(
+ &self.settings.position,
+ &(self.settings.position + direction),
+ &self.settings.up,
+ );
+
+ // Converts camera space to screen space
+ let projection_matrix = {
+ let mut temp = perspective_lh_zo(
+ self.aspect_ratio,
+ self.settings.fov,
+ self.settings.near,
+ self.settings.far,
+ );
+
+ // Vulkan's co-ord system is different from OpenGLs
+ temp[(1, 1)] *= -1.0;
+
+ temp
+ };
+
+ // Chain them together into a single matrix
+ projection_matrix * view_matrix
+ }
+
+ /// Update the aspect ratio
+ pub fn update_aspect_ratio(&mut self, new: f32) {
+ self.aspect_ratio = new;
+ self.is_dirty = true;
+ }
+
+ /// Apply rotation of the camera
+ /// `euler` should be euler angles in degrees
+ pub fn rotate(&mut self, euler: Vector3) {
+ // TODO
+ self.settings.rotation += euler;
+
+ // Clamp -pi/2 < pitch < pi/2
+ if self.settings.rotation.x > R89 {
+ self.settings.rotation.x = R89;
+ } else if self.settings.rotation.x <= -R89 {
+ self.settings.rotation.x = -R89;
+ }
+
+ // -pi < yaw <= pi
+ if self.settings.rotation.y <= -R180 {
+ self.settings.rotation.y = R180 - self.settings.rotation.y % -R180;
+ } else if self.settings.rotation.y > 180.0 {
+ self.settings.rotation.y = -R180 + self.settings.rotation.y % R180;
+ }
+
+ self.is_dirty = true;
+ }
+
+ /// Move the camera by `delta`, relative to the camera's rotation
+ pub fn move_camera_relative(&mut self, delta: Vector3) {
+ let rot_matrix = Mat4::from_euler_angles(
+ -self.settings.rotation.x,
+ self.settings.rotation.y,
+ self.settings.rotation.z,
+ );
+
+ let new = rot_matrix * Vec4::new(delta.x, delta.y, delta.z, 1.0);
+ self.settings.position.x += new.x;
+ self.settings.position.y += new.y;
+ self.settings.position.z += new.z;
+
+ self.is_dirty = true;
+ }
+
+ pub fn camera_pos(&self) -> Vector3 {
+ self.settings.position
+ }
}
#[cfg(test)]
mod tests {
- use stockton_types::Matrix4;
-use stockton_types::Vector3;
- use draw::camera::WorkingCamera;
+ use draw::camera::WorkingCamera;
+ use stockton_types::Matrix4;
+ use stockton_types::Vector3;
- fn contains_nan(mat: &Matrix4) -> bool{
- for x in mat.iter() {
- if *x == std::f32::NAN {
- return true;
- }
- }
- return false;
- }
+ fn contains_nan(mat: &Matrix4) -> bool {
+ for x in mat.iter() {
+ if (*x).is_nan() {
+ return true;
+ }
+ }
+ false
+ }
- #[test]
- fn camera_vp() {
- let mut camera = WorkingCamera::defaults(16.0 / 9.0);
+ #[test]
+ fn camera_vp() {
+ let mut camera = WorkingCamera::defaults(16.0 / 9.0);
- let old = camera.calc_vp_matrix();
- println!("initial vp matrix: {:?}", old);
+ let old = camera.calc_vp_matrix();
+ println!("initial vp matrix: {:?}", old);
- assert!(!contains_nan(&old), "No NaNs for initial matrix");
+ assert!(!contains_nan(&old), "No NaNs for initial matrix");
- // Do a 180
- camera.rotate(Vector3::new(0.0, 180.0, 0.0));
+ // Do a 180
+ camera.rotate(Vector3::new(0.0, 180.0, 0.0));
- let new = camera.calc_vp_matrix();
- assert!(!contains_nan(&new), "No NaNs after rotating");
+ let new = camera.calc_vp_matrix();
+ assert!(!contains_nan(&new), "No NaNs after rotating");
- println!("new vp matrix: {:?}", new);
+ println!("new vp matrix: {:?}", new);
- assert!(old != new, "VP Matrix changes when camera rotates");
- }
+ assert!(old != new, "VP Matrix changes when camera rotates");
+ }
}
diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs
index ed5b5ec..3fff042 100644
--- a/stockton-render/src/draw/context.rs
+++ b/stockton-render/src/draw/context.rs
@@ -17,36 +17,29 @@
//! In the end, this takes in a depth-sorted list of faces and a map file and renders them.
//! You'll need something else to actually find/sort the faces though.
-
use std::{
- mem::{ManuallyDrop, size_of},
- ops::Deref,
- borrow::Borrow,
- convert::TryInto
+ borrow::Borrow,
+ convert::TryInto,
+ mem::{size_of, ManuallyDrop},
+ ops::Deref,
};
use arrayvec::ArrayVec;
-use hal::{
- prelude::*,
- pool::CommandPoolCreateFlags
-};
+use hal::{pool::CommandPoolCreateFlags, prelude::*};
use log::debug;
use winit::window::Window;
-use stockton_types::{Vector2, Vector3};
use stockton_levels::prelude::*;
use stockton_levels::traits::faces::FaceType;
+use stockton_types::{Vector2, Vector3};
-use crate::{
- types::*,
- error
-};
use super::{
- target::{TargetChain, SwapchainProperties},
- camera::WorkingCamera,
- texture::TextureStore,
- buffer::{StagedBuffer, ModifiableBuffer}
+ buffer::{ModifiableBuffer, StagedBuffer},
+ camera::WorkingCamera,
+ target::{SwapchainProperties, TargetChain},
+ texture::TextureStore,
};
+use crate::{error, types::*};
/// Entry point name for shaders
const ENTRY_NAME: &str = "main";
@@ -65,605 +58,697 @@ const FRAGMENT_SOURCE: &str = include_str!("./data/stockton.frag");
/// Represents a point of a triangle, including UV and texture information.
#[derive(Debug, Clone, Copy)]
-pub struct UVPoint (pub Vector3, pub i32, pub Vector2);
+pub struct UVPoint(pub Vector3, pub i32, pub Vector2);
/// Contains all the hal related stuff.
/// In the end, this takes in a depth-sorted list of faces and a map file and renders them.
// TODO: Settings for clear colour, buffer sizes, etc
pub struct RenderingContext<'a> {
- // Parents for most of these things
+ // Parents for most of these things
+ /// Vulkan Instance
+ instance: ManuallyDrop<back::Instance>,
- /// Vulkan Instance
- instance: ManuallyDrop<back::Instance>,
+ /// Device we're using
+ device: ManuallyDrop<Device>,
- /// Device we're using
- device: ManuallyDrop<Device>,
+ /// Adapter we're using
+ adapter: Adapter,
- /// Adapter we're using
- adapter: Adapter,
+ // Render destination
+ /// Surface to draw to
+ surface: ManuallyDrop<Surface>,
- // Render destination
+ /// Swapchain and stuff
+ target_chain: ManuallyDrop<TargetChain>,
- /// Surface to draw to
- surface: ManuallyDrop<Surface>,
+ // Pipeline
+ /// Our main render pass
+ renderpass: ManuallyDrop<RenderPass>,
- /// Swapchain and stuff
- target_chain: ManuallyDrop<TargetChain>,
+ /// The layout of our main graphics pipeline
+ pipeline_layout: ManuallyDrop<PipelineLayout>,
- // Pipeline
+ /// Our main graphics pipeline
+ pipeline: ManuallyDrop<GraphicsPipeline>,
- /// Our main render pass
- renderpass: ManuallyDrop<RenderPass>,
+ // Command pool and buffers
+ /// The command pool used for our buffers
+ cmd_pool: ManuallyDrop<CommandPool>,
- /// The layout of our main graphics pipeline
- pipeline_layout: ManuallyDrop<PipelineLayout>,
+ /// The queue group our buffers belong to
+ queue_group: QueueGroup,
- /// Our main graphics pipeline
- pipeline: ManuallyDrop<GraphicsPipeline>,
+ /// Texture store
+ texture_store: ManuallyDrop<TextureStore>,
- // Command pool and buffers
+ /// (Staged) Vertex Buffer
+ pub vert_buffer: ManuallyDrop<StagedBuffer<'a, UVPoint>>,
- /// The command pool used for our buffers
- cmd_pool: ManuallyDrop<CommandPool>,
+ /// (Staged) Index Buffer
+ pub index_buffer: ManuallyDrop<StagedBuffer<'a, (u16, u16, u16)>>,
- /// The queue group our buffers belong to
- queue_group: QueueGroup,
+ /// Our camera settings
+ camera: WorkingCamera,
- /// Texture store
- texture_store: ManuallyDrop<TextureStore>,
+ /// The vertex shader module
+ vs_module: ManuallyDrop<ShaderModule>,
- /// (Staged) Vertex Buffer
- pub vert_buffer: ManuallyDrop<StagedBuffer<'a, UVPoint>>,
-
- /// (Staged) Index Buffer
- pub index_buffer: ManuallyDrop<StagedBuffer<'a, (u16, u16, u16)>>,
-
- /// Our camera settings
- camera: WorkingCamera,
-
- /// The vertex shader module
- vs_module: ManuallyDrop<ShaderModule>,
-
- /// The fragment shader module
- fs_module: ManuallyDrop<ShaderModule>
+ /// The fragment shader module
+ fs_module: ManuallyDrop<ShaderModule>,
}
impl<'a> RenderingContext<'a> {
- /// Create a new RenderingContext for the given window.
- pub fn new<T: HasTextures>(window: &Window, file: &T) -> Result<Self, error::CreationError> {
- // Create surface
- let (instance, mut surface, mut adapters) = unsafe {
- use hal::Instance;
-
- 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();
-
- (instance, surface, adapters)
- };
-
- // TODO: Properly figure out which adapter to use
- let mut adapter = adapters.remove(0);
-
- // Device & Queue group
- let (mut device, mut 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()
- };
-
- (gpu.device, gpu.queue_groups.pop().unwrap())
- };
-
- // Figure out what our swapchain will look like
- let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface).map_err(|_| error::CreationError::BadSurface)?;
-
- debug!("Detected following swapchain properties: {:?}", swapchain_properties);
-
- // Command pool
- let mut cmd_pool = unsafe {
- device.create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL)
- }.map_err(|_| error::CreationError::OutOfMemoryError)?;
-
- // Renderpass
- let renderpass = {
- use hal::{
- pass::*,
- pso::PipelineStage,
- image::{Access, Layout},
- memory::Dependencies
- };
-
- let img_attachment = Attachment {
- format: Some(swapchain_properties.format),
- samples: 1,
- ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store),
- stencil_ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare),
- layouts: Layout::Undefined..Layout::Present
- };
-
- let depth_attachment = Attachment {
- format: Some(swapchain_properties.depth_format),
- samples: 1,
- ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare),
- stencil_ops: AttachmentOps::new(AttachmentLoadOp::DontCare, AttachmentStoreOp::DontCare),
- layouts: Layout::Undefined..Layout::DepthStencilAttachmentOptimal
- };
-
- let subpass = SubpassDesc {
- colors: &[(0, Layout::ColorAttachmentOptimal)],
- depth_stencil: Some(&(1, Layout::DepthStencilAttachmentOptimal)),
- inputs: &[],
- resolves: &[],
- preserves: &[]
- };
-
- let in_dependency = SubpassDependency {
- flags: Dependencies::empty(),
- passes: None..Some(0),
- stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT..
- (PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS),
- accesses: Access::empty()
- ..(Access::COLOR_ATTACHMENT_READ
- | Access::COLOR_ATTACHMENT_WRITE
- | Access::DEPTH_STENCIL_ATTACHMENT_READ
- | Access::DEPTH_STENCIL_ATTACHMENT_WRITE)
- };
-
- let out_dependency = SubpassDependency {
- flags: Dependencies::empty(),
- passes: Some(0)..None,
- stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS..
- PipelineStage::COLOR_ATTACHMENT_OUTPUT,
- accesses: (Access::COLOR_ATTACHMENT_READ
- | Access::COLOR_ATTACHMENT_WRITE
- | Access::DEPTH_STENCIL_ATTACHMENT_READ
- | Access::DEPTH_STENCIL_ATTACHMENT_WRITE)..
- Access::empty()
- };
-
- unsafe { device.create_render_pass(&[img_attachment, depth_attachment], &[subpass], &[in_dependency, out_dependency]) }
- .map_err(|_| error::CreationError::OutOfMemoryError)?
- };
-
- // Subpass
- let subpass = hal::pass::Subpass {
- index: 0,
- main_pass: &renderpass
- };
-
- // Camera
- // TODO: Settings
- let ratio = swapchain_properties.extent.width as f32 / swapchain_properties.extent.height as f32;
- let camera = WorkingCamera::defaults(ratio);
-
-
- // Vertex and index buffers
- let (vert_buffer, index_buffer) = {
- use hal::buffer::Usage;
-
- let vert = StagedBuffer::new(&mut device, &adapter, Usage::VERTEX, INITIAL_VERT_SIZE)?;
- let index = StagedBuffer::new(&mut device, &adapter, Usage::INDEX, INITIAL_INDEX_SIZE)?;
-
- (vert, index)
- };
-
- // Texture store
- let texture_store = TextureStore::new(&mut device,
- &mut adapter,
- &mut queue_group.queues[0],
- &mut cmd_pool, file)?;
-
- let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new();
- descriptor_set_layouts.push(texture_store.descriptor_set_layout.deref());
-
- // Graphics pipeline
- let (pipeline_layout, pipeline, vs_module, fs_module) = Self::create_pipeline(&mut device, swapchain_properties.extent, &subpass, descriptor_set_layouts)?;
-
- // Swapchain and associated resources
- let target_chain = TargetChain::new(&mut device, &adapter, &mut surface, &renderpass, &mut cmd_pool, swapchain_properties, None).map_err(|e| error::CreationError::TargetChainCreationError (e) )?;
-
- Ok(RenderingContext {
- instance: ManuallyDrop::new(instance),
- surface: ManuallyDrop::new(surface),
-
- device: ManuallyDrop::new(device),
- adapter,
- queue_group,
-
- renderpass: ManuallyDrop::new(renderpass),
- target_chain: ManuallyDrop::new(target_chain),
- cmd_pool: ManuallyDrop::new(cmd_pool),
-
- pipeline_layout: ManuallyDrop::new(pipeline_layout),
- pipeline: ManuallyDrop::new(pipeline),
-
- texture_store: ManuallyDrop::new(texture_store),
-
- vert_buffer: ManuallyDrop::new(vert_buffer),
- index_buffer: ManuallyDrop::new(index_buffer),
-
- vs_module: ManuallyDrop::new(vs_module),
- fs_module: ManuallyDrop::new(fs_module),
-
- camera
- })
- }
-
- /// If this function fails the whole context is probably dead
- /// The context must not be used while this is being called
- pub unsafe fn handle_surface_change(&mut self) -> Result<(), error::CreationError> {
- self.device.wait_idle().unwrap();
-
- let properties = SwapchainProperties::find_best(&self.adapter, &self.surface).map_err(|_| error::CreationError::BadSurface)?;
-
- // Camera settings (aspect ratio)
- self.camera.update_aspect_ratio(
- properties.extent.width as f32 /
- properties.extent.height as f32);
-
- use core::ptr::read;
-
- // Graphics pipeline
- self.device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline)));
-
- self.device
- .destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout)));
-
- self.device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.vs_module)));
- self.device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module)));
-
- let (pipeline_layout, pipeline, vs_module, fs_module) = {
- let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new();
- descriptor_set_layouts.push(self.texture_store.descriptor_set_layout.deref());
-
- let subpass = hal::pass::Subpass {
- index: 0,
- main_pass: &(*self.renderpass)
- };
-
- Self::create_pipeline(&mut self.device, properties.extent, &subpass, descriptor_set_layouts)?
- };
-
- self.pipeline_layout = ManuallyDrop::new(pipeline_layout);
- self.pipeline = ManuallyDrop::new(pipeline);
-
- self.vs_module = ManuallyDrop::new(vs_module);
- self.fs_module = ManuallyDrop::new(fs_module);
-
- let old_swapchain = ManuallyDrop::into_inner(read(&self.target_chain)).deactivate_with_recyling(&mut self.device, &mut self.cmd_pool);
- self.target_chain = ManuallyDrop::new(TargetChain::new(&mut self.device, &self.adapter, &mut self.surface, &self.renderpass, &mut self.cmd_pool, properties, Some(old_swapchain))
- .map_err(|e| error::CreationError::TargetChainCreationError (e) )?);
-
- Ok(())
- }
-
- #[allow(clippy::type_complexity)]
- fn create_pipeline<T>(device: &mut Device, extent: hal::image::Extent, subpass: &hal::pass::Subpass<back::Backend>, set_layouts: T) -> Result<
- (
- PipelineLayout,
- GraphicsPipeline,
- ShaderModule,
- ShaderModule
- ), error::CreationError> where T: IntoIterator, T::Item: Borrow<DescriptorSetLayout> {
- 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", 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", ENTRY_NAME, None)
- .map_err(|e| error::CreationError::ShaderCError (e))?;
-
- // Make into shader module
- unsafe {
- (device
- .create_shader_module(vertex_compile_artifact.as_binary())
- .map_err(|e| error::CreationError::ShaderModuleFailed (e))?,
- device
- .create_shader_module(fragment_compile_artifact.as_binary())
- .map_err(|e| error::CreationError::ShaderModuleFailed (e))?)
- }
- };
-
- // 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>() * 6) as u32,
- rate: VertexInputRate::Vertex,
- }];
-
- let attributes: Vec<AttributeDesc> = pipeline_vb_attributes!(0,
- size_of::<f32>() * 3; Rgb32Sfloat,
- size_of::<u32>(); R32Sint,
- size_of::<f32>() * 2; Rg32Sfloat
- );
-
- // Rasterizer
- let rasterizer = Rasterizer {
- polygon_mode: PolygonMode::Fill,
- cull_face: Face::BACK,
- front_face: FrontFace::CounterClockwise,
- depth_clamping: false,
- depth_bias: None,
- conservative: true,
- line_width: hal::pso::State::Static(1.0)
- };
-
- // Depth stencil
- let depth_stencil = DepthStencilDesc {
- depth: Some(DepthTest {
- fun: Comparison::Less,
- write: true
- }),
- depth_bounds: false,
- stencil: None,
- };
-
- // Pipeline layout
- let layout = unsafe {
- device.create_pipeline_layout(
- set_layouts,
- // vp matrix, 4x4 f32
- &[(ShaderStageFlags::VERTEX, 0..64)]
- )
- }.map_err(|_| error::CreationError::OutOfMemoryError)?;
-
- // Colour blending
- let blender = {
- let blend_state = BlendState {
- color: BlendOp::Add {
- src: Factor::One,
- dst: Factor::Zero,
- },
- alpha: BlendOp::Add {
- src: Factor::One,
- dst: Factor::Zero,
- },
- };
-
- BlendDesc {
- logic_op: Some(LogicOp::Copy),
- targets: vec![ColorBlendDesc { mask: ColorMask::ALL, blend: Some(blend_state) }],
- }
- };
-
- // Baked states
- let baked_states = BakedStates {
- viewport: Some(Viewport {
- rect: extent.rect(),
- depth: (0.0..1.0)
- }),
- scissor: Some(extent.rect()),
- blend_color: None,
- depth_bounds: None,
- };
-
- // Input assembler
- let input_assembler = InputAssemblerDesc::new(Primitive::TriangleList);
-
- // Pipeline description
- let pipeline_desc = GraphicsPipelineDesc {
- shaders,
- rasterizer,
- vertex_buffers,
- blender,
- depth_stencil,
- multisampling: None,
- baked_states,
- layout: &layout,
- subpass: *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((layout, pipeline, vs_module, fs_module))
- }
-
- /// Draw all vertices in the buffer
- pub fn draw_vertices<M: MinBSPFeatures<VulkanSystem>>(&mut self, file: &M,faces: &Vec<u32>) -> Result<(), &'static str> {
- // Prepare command buffer
- let cmd_buffer = self.target_chain.prep_next_target(
- &mut self.device,
- self.vert_buffer.get_buffer(),
- self.index_buffer.get_buffer(),
- &self.renderpass,
- &self.pipeline,
- &self.pipeline_layout,
- &mut self.camera
- )?;
-
- // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once.
- let mut current_chunk = file.get_face(0).texture_idx as usize / 8;
- let mut chunk_start = 0;
-
- let mut curr_vert_idx: usize = 0;
- let mut curr_idx_idx: usize = 0;
-
- for face in faces.into_iter().map(|idx| file.get_face(*idx)) {
- if current_chunk != face.texture_idx as usize / 8 {
- // Last index was last of group, so draw it all.
- let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new();
- descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk));
- unsafe {
-
- cmd_buffer.bind_graphics_descriptor_sets(
- &self.pipeline_layout,
- 0,
- descriptor_sets,
- &[]
- );
- cmd_buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1);
- }
-
- // Next group of same-chunked faces starts here.
- chunk_start = curr_idx_idx;
- current_chunk = face.texture_idx as usize / 8;
- }
-
- if face.face_type == FaceType::Polygon || face.face_type == FaceType::Mesh {
- // 2 layers of indirection
- let base = face.vertices_idx.start;
-
- for idx in face.meshverts_idx.clone().step_by(3) {
- let start_idx: u16 = curr_vert_idx.try_into().unwrap();
-
- for idx2 in idx..idx+3 {
- let vert = &file.resolve_meshvert(idx2 as u32, base);
- let uv = Vector2::new(vert.tex.u[0], vert.tex.v[0]);
-
- let uvp = UVPoint (vert.position, face.texture_idx.try_into().unwrap(), uv);
- self.vert_buffer[curr_vert_idx] = uvp;
-
- curr_vert_idx += 1;
- }
-
-
- self.index_buffer[curr_idx_idx] = (start_idx, start_idx + 1, start_idx + 2);
-
- curr_idx_idx += 1;
-
- if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() {
- println!("out of vertex buffer space!");
- break;
- }
- }
- } else {
- // TODO: Other types of faces
- }
-
- if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() {
- println!("out of vertex buffer space!");
- break;
- }
- }
-
- // Draw the final group of chunks
- let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new();
- descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk));
- unsafe {
- cmd_buffer.bind_graphics_descriptor_sets(
- &self.pipeline_layout,
- 0,
- descriptor_sets,
- &[]
- );
- cmd_buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1);
- }
-
- // Update our buffers before we actually start drawing
- self.vert_buffer.commit(
- &self.device,
- &mut self.queue_group.queues[0],
- &mut self.cmd_pool
- );
-
- self.index_buffer.commit(
- &self.device,
- &mut self.queue_group.queues[0],
- &mut self.cmd_pool
- );
-
- // Send commands off to GPU
- self.target_chain.finish_and_submit_target(&mut self.queue_group.queues[0])?;
-
- Ok(())
- }
-
- /// Get current position of camera
- pub fn camera_pos(&self) -> Vector3 {
- self.camera.camera_pos()
- }
-
- /// Move the camera by `delta` relative to its rotation
- pub fn move_camera_relative(&mut self, delta: Vector3) {
- self.camera.move_camera_relative(delta)
- }
-
- /// Rotate the camera
- /// `euler` should be euler angles in radians
- pub fn rotate(&mut self, euler: Vector3) {
- self.camera.rotate(euler)
- }
+ /// Create a new RenderingContext for the given window.
+ pub fn new<T: HasTextures>(window: &Window, file: &T) -> Result<Self, error::CreationError> {
+ // Create surface
+ let (instance, mut surface, mut adapters) = unsafe {
+ use hal::Instance;
+
+ 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();
+
+ (instance, surface, adapters)
+ };
+
+ // TODO: Properly figure out which adapter to use
+ let mut adapter = adapters.remove(0);
+
+ // Device & Queue group
+ let (mut device, mut 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()
+ };
+
+ (gpu.device, gpu.queue_groups.pop().unwrap())
+ };
+
+ // Figure out what our swapchain will look like
+ let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface)
+ .map_err(|_| error::CreationError::BadSurface)?;
+
+ debug!(
+ "Detected following swapchain properties: {:?}",
+ swapchain_properties
+ );
+
+ // Command pool
+ let mut cmd_pool = unsafe {
+ device.create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL)
+ }
+ .map_err(|_| error::CreationError::OutOfMemoryError)?;
+
+ // Renderpass
+ let renderpass = {
+ use hal::{
+ image::{Access, Layout},
+ memory::Dependencies,
+ pass::*,
+ pso::PipelineStage,
+ };
+
+ let img_attachment = Attachment {
+ format: Some(swapchain_properties.format),
+ samples: 1,
+ ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store),
+ stencil_ops: AttachmentOps::new(
+ AttachmentLoadOp::Clear,
+ AttachmentStoreOp::DontCare,
+ ),
+ layouts: Layout::Undefined..Layout::Present,
+ };
+
+ let depth_attachment = Attachment {
+ format: Some(swapchain_properties.depth_format),
+ samples: 1,
+ ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare),
+ stencil_ops: AttachmentOps::new(
+ AttachmentLoadOp::DontCare,
+ AttachmentStoreOp::DontCare,
+ ),
+ layouts: Layout::Undefined..Layout::DepthStencilAttachmentOptimal,
+ };
+
+ let subpass = SubpassDesc {
+ colors: &[(0, Layout::ColorAttachmentOptimal)],
+ depth_stencil: Some(&(1, Layout::DepthStencilAttachmentOptimal)),
+ inputs: &[],
+ resolves: &[],
+ preserves: &[],
+ };
+
+ let in_dependency = SubpassDependency {
+ flags: Dependencies::empty(),
+ passes: None..Some(0),
+ stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT
+ ..(PipelineStage::COLOR_ATTACHMENT_OUTPUT
+ | PipelineStage::EARLY_FRAGMENT_TESTS),
+ accesses: Access::empty()
+ ..(Access::COLOR_ATTACHMENT_READ
+ | Access::COLOR_ATTACHMENT_WRITE
+ | Access::DEPTH_STENCIL_ATTACHMENT_READ
+ | Access::DEPTH_STENCIL_ATTACHMENT_WRITE),
+ };
+
+ let out_dependency = SubpassDependency {
+ flags: Dependencies::empty(),
+ passes: Some(0)..None,
+ stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS
+ ..PipelineStage::COLOR_ATTACHMENT_OUTPUT,
+ accesses: (Access::COLOR_ATTACHMENT_READ
+ | Access::COLOR_ATTACHMENT_WRITE
+ | Access::DEPTH_STENCIL_ATTACHMENT_READ
+ | Access::DEPTH_STENCIL_ATTACHMENT_WRITE)
+ ..Access::empty(),
+ };
+
+ unsafe {
+ device.create_render_pass(
+ &[img_attachment, depth_attachment],
+ &[subpass],
+ &[in_dependency, out_dependency],
+ )
+ }
+ .map_err(|_| error::CreationError::OutOfMemoryError)?
+ };
+
+ // Subpass
+ let subpass = hal::pass::Subpass {
+ index: 0,
+ main_pass: &renderpass,
+ };
+
+ // Camera
+ // TODO: Settings
+ let ratio =
+ swapchain_properties.extent.width as f32 / swapchain_properties.extent.height as f32;
+ let camera = WorkingCamera::defaults(ratio);
+
+ // Vertex and index buffers
+ let (vert_buffer, index_buffer) = {
+ use hal::buffer::Usage;
+
+ let vert = StagedBuffer::new(&mut device, &adapter, Usage::VERTEX, INITIAL_VERT_SIZE)?;
+ let index = StagedBuffer::new(&mut device, &adapter, Usage::INDEX, INITIAL_INDEX_SIZE)?;
+
+ (vert, index)
+ };
+
+ // Texture store
+ let texture_store = TextureStore::new(
+ &mut device,
+ &mut adapter,
+ &mut queue_group.queues[0],
+ &mut cmd_pool,
+ file,
+ )?;
+
+ let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new();
+ descriptor_set_layouts.push(texture_store.descriptor_set_layout.deref());
+
+ // Graphics pipeline
+ let (pipeline_layout, pipeline, vs_module, fs_module) = Self::create_pipeline(
+ &mut device,
+ swapchain_properties.extent,
+ &subpass,
+ descriptor_set_layouts,
+ )?;
+
+ // Swapchain and associated resources
+ let target_chain = TargetChain::new(
+ &mut device,
+ &adapter,
+ &mut surface,
+ &renderpass,
+ &mut cmd_pool,
+ swapchain_properties,
+ None,
+ )
+ .map_err(error::CreationError::TargetChainCreationError)?;
+
+ Ok(RenderingContext {
+ instance: ManuallyDrop::new(instance),
+ surface: ManuallyDrop::new(surface),
+
+ device: ManuallyDrop::new(device),
+ adapter,
+ queue_group,
+
+ renderpass: ManuallyDrop::new(renderpass),
+ target_chain: ManuallyDrop::new(target_chain),
+ cmd_pool: ManuallyDrop::new(cmd_pool),
+
+ pipeline_layout: ManuallyDrop::new(pipeline_layout),
+ pipeline: ManuallyDrop::new(pipeline),
+
+ texture_store: ManuallyDrop::new(texture_store),
+
+ vert_buffer: ManuallyDrop::new(vert_buffer),
+ index_buffer: ManuallyDrop::new(index_buffer),
+
+ vs_module: ManuallyDrop::new(vs_module),
+ fs_module: ManuallyDrop::new(fs_module),
+
+ camera,
+ })
+ }
+
+ /// If this function fails the whole context is probably dead
+ /// # Safety
+ /// The context must not be used while this is being called
+ pub unsafe fn handle_surface_change(&mut self) -> Result<(), error::CreationError> {
+ self.device.wait_idle().unwrap();
+
+ let properties = SwapchainProperties::find_best(&self.adapter, &self.surface)
+ .map_err(|_| error::CreationError::BadSurface)?;
+
+ // Camera settings (aspect ratio)
+ self.camera
+ .update_aspect_ratio(properties.extent.width as f32 / properties.extent.height as f32);
+
+ use core::ptr::read;
+
+ // Graphics pipeline
+ self.device
+ .destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline)));
+
+ self.device
+ .destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout)));
+
+ self.device
+ .destroy_shader_module(ManuallyDrop::into_inner(read(&self.vs_module)));
+ self.device
+ .destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module)));
+
+ let (pipeline_layout, pipeline, vs_module, fs_module) = {
+ let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new();
+ descriptor_set_layouts.push(self.texture_store.descriptor_set_layout.deref());
+
+ let subpass = hal::pass::Subpass {
+ index: 0,
+ main_pass: &(*self.renderpass),
+ };
+
+ Self::create_pipeline(
+ &mut self.device,
+ properties.extent,
+ &subpass,
+ descriptor_set_layouts,
+ )?
+ };
+
+ self.pipeline_layout = ManuallyDrop::new(pipeline_layout);
+ self.pipeline = ManuallyDrop::new(pipeline);
+
+ self.vs_module = ManuallyDrop::new(vs_module);
+ self.fs_module = ManuallyDrop::new(fs_module);
+
+ let old_swapchain = ManuallyDrop::into_inner(read(&self.target_chain))
+ .deactivate_with_recyling(&mut self.device, &mut self.cmd_pool);
+ self.target_chain = ManuallyDrop::new(
+ TargetChain::new(
+ &mut self.device,
+ &self.adapter,
+ &mut self.surface,
+ &self.renderpass,
+ &mut self.cmd_pool,
+ properties,
+ Some(old_swapchain),
+ )
+ .map_err(error::CreationError::TargetChainCreationError)?,
+ );
+
+ Ok(())
+ }
+
+ #[allow(clippy::type_complexity)]
+ fn create_pipeline<T>(
+ device: &mut Device,
+ extent: hal::image::Extent,
+ subpass: &hal::pass::Subpass<back::Backend>,
+ set_layouts: T,
+ ) -> Result<(PipelineLayout, GraphicsPipeline, ShaderModule, ShaderModule), error::CreationError>
+ where
+ T: IntoIterator,
+ T::Item: Borrow<DescriptorSetLayout>,
+ {
+ use hal::format::Format;
+ use hal::pso::*;
+
+ // Shader modules
+ let (vs_module, fs_module) = {
+ let mut compiler = shaderc::Compiler::new().ok_or(error::CreationError::NoShaderC)?;
+
+ let vertex_compile_artifact = compiler
+ .compile_into_spirv(
+ VERTEX_SOURCE,
+ shaderc::ShaderKind::Vertex,
+ "vertex.vert",
+ ENTRY_NAME,
+ None,
+ )
+ .map_err(error::CreationError::ShaderCError)?;
+
+ let fragment_compile_artifact = compiler
+ .compile_into_spirv(
+ FRAGMENT_SOURCE,
+ shaderc::ShaderKind::Fragment,
+ "fragment.frag",
+ ENTRY_NAME,
+ None,
+ )
+ .map_err(error::CreationError::ShaderCError)?;
+
+ // Make into shader module
+ unsafe {
+ (
+ device
+ .create_shader_module(vertex_compile_artifact.as_binary())
+ .map_err(error::CreationError::ShaderModuleFailed)?,
+ device
+ .create_shader_module(fragment_compile_artifact.as_binary())
+ .map_err(error::CreationError::ShaderModuleFailed)?,
+ )
+ }
+ };
+
+ // Shader entry points (ShaderStage)
+ let (vs_entry, fs_entry) = (
+ EntryPoint::<back::Backend> {
+ entry: ENTRY_NAME,
+ module: &vs_module,
+ specialization: Specialization::default(),
+ },
+ EntryPoint::<back::Backend> {
+ entry: ENTRY_NAME,
+ module: &fs_module,
+ specialization: Specialization::default(),
+ },
+ );
+
+ // Shader set
+ let shaders = GraphicsShaderSet {
+ vertex: vs_entry,
+ fragment: Some(fs_entry),
+ hull: None,
+ domain: None,
+ geometry: None,
+ };
+
+ // Vertex buffers
+ let vertex_buffers: Vec<VertexBufferDesc> = vec![VertexBufferDesc {
+ binding: 0,
+ stride: (size_of::<f32>() * 6) as u32,
+ rate: VertexInputRate::Vertex,
+ }];
+
+ let attributes: Vec<AttributeDesc> = pipeline_vb_attributes!(0,
+ size_of::<f32>() * 3; Rgb32Sfloat,
+ size_of::<u32>(); R32Sint,
+ size_of::<f32>() * 2; Rg32Sfloat
+ );
+
+ // Rasterizer
+ let rasterizer = Rasterizer {
+ polygon_mode: PolygonMode::Fill,
+ cull_face: Face::BACK,
+ front_face: FrontFace::CounterClockwise,
+ depth_clamping: false,
+ depth_bias: None,
+ conservative: true,
+ line_width: hal::pso::State::Static(1.0),
+ };
+
+ // Depth stencil
+ let depth_stencil = DepthStencilDesc {
+ depth: Some(DepthTest {
+ fun: Comparison::Less,
+ write: true,
+ }),
+ depth_bounds: false,
+ stencil: None,
+ };
+
+ // Pipeline layout
+ let layout = unsafe {
+ device.create_pipeline_layout(
+ set_layouts,
+ // vp matrix, 4x4 f32
+ &[(ShaderStageFlags::VERTEX, 0..64)],
+ )
+ }
+ .map_err(|_| error::CreationError::OutOfMemoryError)?;
+
+ // Colour blending
+ let blender = {
+ let blend_state = BlendState {
+ color: BlendOp::Add {
+ src: Factor::One,
+ dst: Factor::Zero,
+ },
+ alpha: BlendOp::Add {
+ src: Factor::One,
+ dst: Factor::Zero,
+ },
+ };
+
+ BlendDesc {
+ logic_op: Some(LogicOp::Copy),
+ targets: vec![ColorBlendDesc {
+ mask: ColorMask::ALL,
+ blend: Some(blend_state),
+ }],
+ }
+ };
+
+ // Baked states
+ let baked_states = BakedStates {
+ viewport: Some(Viewport {
+ rect: extent.rect(),
+ depth: (0.0..1.0),
+ }),
+ scissor: Some(extent.rect()),
+ blend_color: None,
+ depth_bounds: None,
+ };
+
+ // Input assembler
+ let input_assembler = InputAssemblerDesc::new(Primitive::TriangleList);
+
+ // Pipeline description
+ let pipeline_desc = GraphicsPipelineDesc {
+ shaders,
+ rasterizer,
+ vertex_buffers,
+ blender,
+ depth_stencil,
+ multisampling: None,
+ baked_states,
+ layout: &layout,
+ subpass: *subpass,
+ flags: PipelineCreationFlags::empty(),
+ parent: BasePipeline::None,
+ input_assembler,
+ attributes,
+ };
+
+ // Pipeline
+ let pipeline = unsafe { device.create_graphics_pipeline(&pipeline_desc, None) }
+ .map_err(error::CreationError::PipelineError)?;
+
+ Ok((layout, pipeline, vs_module, fs_module))
+ }
+
+ /// Draw all vertices in the buffer
+ pub fn draw_vertices<M: MinBSPFeatures<VulkanSystem>>(
+ &mut self,
+ file: &M,
+ faces: &[u32],
+ ) -> Result<(), &'static str> {
+ // Prepare command buffer
+ let cmd_buffer = self.target_chain.prep_next_target(
+ &mut self.device,
+ self.vert_buffer.get_buffer(),
+ self.index_buffer.get_buffer(),
+ &self.renderpass,
+ &self.pipeline,
+ &self.pipeline_layout,
+ &mut self.camera,
+ )?;
+
+ // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once.
+ let mut current_chunk = file.get_face(0).texture_idx as usize / 8;
+ let mut chunk_start = 0;
+
+ let mut curr_vert_idx: usize = 0;
+ let mut curr_idx_idx: usize = 0;
+
+ for face in faces.iter().map(|idx| file.get_face(*idx)) {
+ if current_chunk != face.texture_idx as usize / 8 {
+ // Last index was last of group, so draw it all.
+ let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new();
+ descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk));
+ unsafe {
+ cmd_buffer.bind_graphics_descriptor_sets(
+ &self.pipeline_layout,
+ 0,
+ descriptor_sets,
+ &[],
+ );
+ cmd_buffer.draw_indexed(
+ chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1,
+ 0,
+ 0..1,
+ );
+ }
+
+ // Next group of same-chunked faces starts here.
+ chunk_start = curr_idx_idx;
+ current_chunk = face.texture_idx as usize / 8;
+ }
+
+ if face.face_type == FaceType::Polygon || face.face_type == FaceType::Mesh {
+ // 2 layers of indirection
+ let base = face.vertices_idx.start;
+
+ for idx in face.meshverts_idx.clone().step_by(3) {
+ let start_idx: u16 = curr_vert_idx.try_into().unwrap();
+
+ for idx2 in idx..idx + 3 {
+ let vert = &file.resolve_meshvert(idx2 as u32, base);
+ let uv = Vector2::new(vert.tex.u[0], vert.tex.v[0]);
+
+ let uvp = UVPoint(vert.position, face.texture_idx.try_into().unwrap(), uv);
+ self.vert_buffer[curr_vert_idx] = uvp;
+
+ curr_vert_idx += 1;
+ }
+
+ self.index_buffer[curr_idx_idx] = (start_idx, start_idx + 1, start_idx + 2);
+
+ curr_idx_idx += 1;
+
+ if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap()
+ || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap()
+ {
+ println!("out of vertex buffer space!");
+ break;
+ }
+ }
+ } else {
+ // TODO: Other types of faces
+ }
+
+ if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap()
+ || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap()
+ {
+ println!("out of vertex buffer space!");
+ break;
+ }
+ }
+
+ // Draw the final group of chunks
+ let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new();
+ descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk));
+ unsafe {
+ cmd_buffer.bind_graphics_descriptor_sets(
+ &self.pipeline_layout,
+ 0,
+ descriptor_sets,
+ &[],
+ );
+ cmd_buffer.draw_indexed(
+ chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1,
+ 0,
+ 0..1,
+ );
+ }
+
+ // Update our buffers before we actually start drawing
+ self.vert_buffer.commit(
+ &self.device,
+ &mut self.queue_group.queues[0],
+ &mut self.cmd_pool,
+ );
+
+ self.index_buffer.commit(
+ &self.device,
+ &mut self.queue_group.queues[0],
+ &mut self.cmd_pool,
+ );
+
+ // Send commands off to GPU
+ self.target_chain
+ .finish_and_submit_target(&mut self.queue_group.queues[0])?;
+
+ Ok(())
+ }
+
+ /// Get current position of camera
+ pub fn camera_pos(&self) -> Vector3 {
+ self.camera.camera_pos()
+ }
+
+ /// Move the camera by `delta` relative to its rotation
+ pub fn move_camera_relative(&mut self, delta: Vector3) {
+ self.camera.move_camera_relative(delta)
+ }
+
+ /// Rotate the camera
+ /// `euler` should be euler angles in radians
+ pub fn rotate(&mut self, euler: Vector3) {
+ self.camera.rotate(euler)
+ }
}
impl<'a> core::ops::Drop for RenderingContext<'a> {
- fn drop(&mut self) {
- self.device.wait_idle().unwrap();
-
- unsafe {
- 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);
- ManuallyDrop::into_inner(read(&self.texture_store)).deactivate(&mut self.device);
-
- ManuallyDrop::into_inner(read(&self.target_chain)).deactivate(&mut self.device, &mut self.cmd_pool);
-
- 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_shader_module(ManuallyDrop::into_inner(read(&self.vs_module)));
- self.device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module)));
-
- self.device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline)));
-
- 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
+ fn drop(&mut self) {
+ self.device.wait_idle().unwrap();
+
+ unsafe {
+ 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);
+ ManuallyDrop::into_inner(read(&self.texture_store)).deactivate(&mut self.device);
+
+ ManuallyDrop::into_inner(read(&self.target_chain))
+ .deactivate(&mut self.device, &mut self.cmd_pool);
+
+ 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_shader_module(ManuallyDrop::into_inner(read(&self.vs_module)));
+ self.device
+ .destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module)));
+
+ self.device
+ .destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline)));
+
+ 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);
+ }
+ }
+}
diff --git a/stockton-render/src/draw/macros.rs b/stockton-render/src/draw/macros.rs
index d191d5b..65566a2 100644
--- a/stockton-render/src/draw/macros.rs
+++ b/stockton-render/src/draw/macros.rs
@@ -19,10 +19,10 @@
/// ```
/// // 0 is the binding value
/// let attributes: Vec<AttributeDesc> = pipeline_vb_attributes!(0,
-/// size_of::<f32>() * 3; Rgb32Sfloat
-/// size_of::<f32>() * 2; Rg32Sfloat,
-/// size_of::<u32>(); R32Sint
-/// );
+/// size_of::<f32>() * 3; Rgb32Sfloat
+/// size_of::<f32>() * 2; Rg32Sfloat,
+/// size_of::<u32>(); R32Sint
+/// );
/// ```
/// See the hal::pso::Format enum for possible types
macro_rules! pipeline_vb_attributes {
@@ -98,4 +98,4 @@ macro_rules! pipeline_vb_attributes {
}
});
});
-} \ No newline at end of file
+}
diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs
index af86b46..1151192 100644
--- a/stockton-render/src/draw/mod.rs
+++ b/stockton-render/src/draw/mod.rs
@@ -19,10 +19,10 @@ pub mod target;
#[macro_use]
mod macros;
-mod context;
mod buffer;
-mod texture;
mod camera;
+mod context;
+mod texture;
pub use self::context::RenderingContext;
pub use self::context::UVPoint;
diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs
index 3d0ecdf..5e0ade0 100644
--- a/stockton-render/src/draw/target.rs
+++ b/stockton-render/src/draw/target.rs
@@ -14,394 +14,498 @@
// with this program. If not, see <http://www.gnu.org/licenses/>.
//! Resources needed for drawing on the screen, including sync objects
+use super::{camera::WorkingCamera, texture::image::LoadedImage};
use crate::types::*;
-use super::{
- camera::WorkingCamera,
- texture::image::LoadedImage
-};
-use core::{
- iter::once,
- mem::ManuallyDrop
-};
+use core::{iter::once, mem::ManuallyDrop};
use arrayvec::ArrayVec;
use hal::{
- prelude::*,
- queue::Submission,
- pso::Viewport,
- format::{ChannelType, Format, Swizzle},
- image::{Extent, ViewKind, Usage as ImgUsage},
- window::{CompositeAlphaMode, PresentMode, SwapchainConfig, Extent2D}
+ format::{ChannelType, Format, Swizzle},
+ image::{Extent, Usage as ImgUsage, ViewKind},
+ prelude::*,
+ pso::Viewport,
+ queue::Submission,
+ window::{CompositeAlphaMode, Extent2D, PresentMode, SwapchainConfig},
};
/// 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,
+ aspects: hal::format::Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
};
#[derive(Debug, Clone)]
pub struct SwapchainProperties {
- pub format: Format,
- pub depth_format: Format,
- pub present_mode: PresentMode,
- pub composite_alpha_mode: CompositeAlphaMode,
- pub viewport: Viewport,
- pub extent: Extent
+ pub format: Format,
+ pub depth_format: Format,
+ pub present_mode: PresentMode,
+ pub composite_alpha_mode: CompositeAlphaMode,
+ pub viewport: Viewport,
+ pub extent: Extent,
}
impl SwapchainProperties {
- pub fn find_best(adapter: &Adapter, surface: &Surface) -> Result<SwapchainProperties, ()> {
- 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.clone().map_or(Format::Rgba8Srgb, |formats| {
- formats.iter()
- .find(|format| format.base_format().1 == ChannelType::Srgb)
- .map(|format| *format)
- .unwrap_or(formats[0])
- });
-
- let depth_format = *[Format::D32SfloatS8Uint, Format::D24UnormS8Uint, Format::D32Sfloat].iter().find(|format| {
- use hal::format::ImageFeature;
-
- format.is_depth() && adapter.physical_device.format_properties(Some(**format)).optimal_tiling.contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT)
- }).ok_or_else(|| ())?;
-
- let present_mode = {
- [PresentMode::MAILBOX, PresentMode::FIFO, PresentMode::RELAXED, PresentMode::IMMEDIATE]
- .iter()
- .cloned()
- .find(|pm| caps.present_modes.contains(*pm))
- .ok_or(())?
- };
- let composite_alpha_mode = {
- [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED]
- .iter()
- .cloned()
- .find(|ca| caps.composite_alpha_modes.contains(*ca))
- .ok_or(())?
- };
-
- let extent = caps.extents.end().to_extent(); // Size
- let viewport = Viewport {
- rect: extent.rect(),
- depth: 0.0..1.0
- };
-
- Ok(SwapchainProperties {format, depth_format, present_mode, composite_alpha_mode, extent, viewport})
- }
+ pub fn find_best(adapter: &Adapter, surface: &Surface) -> Result<SwapchainProperties, ()> {
+ 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)
+ .copied()
+ .unwrap_or(formats[0])
+ });
+
+ let depth_format = *[
+ Format::D32SfloatS8Uint,
+ Format::D24UnormS8Uint,
+ Format::D32Sfloat,
+ ]
+ .iter()
+ .find(|format| {
+ use hal::format::ImageFeature;
+
+ format.is_depth()
+ && adapter
+ .physical_device
+ .format_properties(Some(**format))
+ .optimal_tiling
+ .contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT)
+ })
+ .ok_or_else(|| ())?;
+
+ let present_mode = {
+ [
+ PresentMode::MAILBOX,
+ PresentMode::FIFO,
+ PresentMode::RELAXED,
+ PresentMode::IMMEDIATE,
+ ]
+ .iter()
+ .cloned()
+ .find(|pm| caps.present_modes.contains(*pm))
+ .ok_or(())?
+ };
+ let composite_alpha_mode = {
+ [
+ CompositeAlphaMode::OPAQUE,
+ CompositeAlphaMode::INHERIT,
+ CompositeAlphaMode::PREMULTIPLIED,
+ CompositeAlphaMode::POSTMULTIPLIED,
+ ]
+ .iter()
+ .cloned()
+ .find(|ca| caps.composite_alpha_modes.contains(*ca))
+ .ok_or(())?
+ };
+
+ let extent = caps.extents.end().to_extent(); // Size
+ let viewport = Viewport {
+ rect: extent.rect(),
+ depth: 0.0..1.0,
+ };
+
+ Ok(SwapchainProperties {
+ format,
+ depth_format,
+ present_mode,
+ composite_alpha_mode,
+ extent,
+ viewport,
+ })
+ }
}
pub struct TargetChain {
- /// Swapchain we're targeting
- pub swapchain: ManuallyDrop<Swapchain>,
+ /// Swapchain we're targeting
+ pub swapchain: ManuallyDrop<Swapchain>,
- pub properties: SwapchainProperties,
+ pub properties: SwapchainProperties,
- /// The depth buffer/image used for drawing
- pub depth_buffer: ManuallyDrop<LoadedImage>,
+ /// The depth buffer/image used for drawing
+ pub depth_buffer: ManuallyDrop<LoadedImage>,
- /// Resources tied to each target frame in the swapchain
- pub targets: Box<[TargetResources]>,
+ /// Resources tied to each target frame in the swapchain
+ pub targets: Box<[TargetResources]>,
- /// The last target drawn to
- last_drawn: usize,
+ /// The last target drawn to
+ last_drawn: usize,
- /// Last image index of the swapchain drawn to
- last_image_index: u32,
+ /// Last image index of the swapchain drawn to
+ last_image_index: u32,
}
impl TargetChain {
- pub fn new(device: &mut Device, adapter: &Adapter, surface: &mut Surface, renderpass: &RenderPass, cmd_pool: &mut CommandPool, properties: SwapchainProperties, old_swapchain: Option<Swapchain>) -> Result<TargetChain, TargetChainCreationError> {
- let caps = surface.capabilities(&adapter.physical_device);
-
- // Number of frames to pre-render
- let image_count = if properties.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))
- };
-
- // Swap config
- let swap_config = SwapchainConfig {
- present_mode: properties.present_mode,
- composite_alpha_mode: properties.composite_alpha_mode,
- format: properties.format,
- extent: Extent2D { width: properties.extent.width, height: properties.extent.height },
- image_count,
- image_layers: 1,
- image_usage: ImgUsage::COLOR_ATTACHMENT,
- };
-
- // Swapchain
- let (swapchain, mut backbuffer) = unsafe {
- device.create_swapchain(surface, swap_config, old_swapchain)
- .map_err(|_| TargetChainCreationError::Todo)?
- };
-
- let depth_buffer: LoadedImage = {
- use hal::image::SubresourceRange;
- use hal::format::Aspects;
-
- LoadedImage::new(device, adapter, properties.depth_format, ImgUsage::DEPTH_STENCIL_ATTACHMENT, SubresourceRange {
- aspects: Aspects::DEPTH,
- levels: 0..1,
- layers: 0..1,
- },properties.extent.width as usize, properties.extent.height as usize).map_err(|_| TargetChainCreationError::Todo)
- }?;
-
- let mut targets: Vec<TargetResources> = Vec::with_capacity(backbuffer.len());
- for image in backbuffer.drain(..) {
- targets.push(TargetResources::new(device, cmd_pool, renderpass, image, &(*depth_buffer.image_view), properties.extent, properties.format)
- .map_err(|_| TargetChainCreationError::Todo)?);
- }
-
- Ok(TargetChain {
- swapchain: ManuallyDrop::new(swapchain),
- targets: targets.into_boxed_slice(),
- depth_buffer: ManuallyDrop::new(depth_buffer),
- properties,
- last_drawn: (image_count - 1) as usize, // This means the next one to be used is index 0
- last_image_index: 0
- })
- }
-
- pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) -> () {
- use core::ptr::read;
- unsafe {
- ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device);
-
- for i in 0..self.targets.len() {
- read(&self.targets[i]).deactivate(device, cmd_pool);
- }
-
- device.destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain)));
- }
- }
-
- pub fn deactivate_with_recyling(self, device: &mut Device, cmd_pool: &mut CommandPool) -> Swapchain {
- use core::ptr::read;
- unsafe {
- ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device);
-
- for i in 0..self.targets.len() {
- read(&self.targets[i]).deactivate(device, cmd_pool);
- }
- }
-
- return unsafe { ManuallyDrop::into_inner(read(&self.swapchain)) };
- }
-
- pub fn prep_next_target<'a>(&'a mut self, device: &mut Device, vert_buffer: &Buffer, index_buffer: &Buffer, renderpass: &RenderPass, pipeline: &GraphicsPipeline, pipeline_layout: &PipelineLayout, camera: &mut WorkingCamera) -> Result<&'a mut crate::types::CommandBuffer, &'static str> {
- self.last_drawn = (self.last_drawn + 1) % self.targets.len();
-
- let target = &mut self.targets[self.last_drawn];
-
- // Get the image
- let (image_index, _) = unsafe {
- self.swapchain
- .acquire_image(core::u64::MAX, Some(&target.get_image), None)
- .map_err(|_| "FrameError::AcquireError")?
- };
-
- self.last_image_index = image_index;
-
- // Make sure whatever was last using this has finished
- unsafe {
- device
- .wait_for_fence(&target.present_complete, core::u64::MAX)
- .map_err(|_| "FrameError::SyncObjectError")?;
- device
- .reset_fence(&target.present_complete)
- .map_err(|_| "FrameError::SyncObjectError")?;
- };
-
- // Record commands
- unsafe {
- use hal::buffer::{IndexBufferView, SubRange};
- use hal::command::{SubpassContents, CommandBufferFlags, ClearValue, ClearColor, ClearDepthStencil};
- use hal::pso::ShaderStageFlags;
-
- // Colour to clear window to
- let clear_values = [ClearValue {
- color: ClearColor {
- float32: [0.0, 0.0, 0.0, 1.0]
- }
- }, ClearValue {
- depth_stencil: ClearDepthStencil {
- depth: 1.0,
- stencil: 0
- }
- }];
-
- // Get references to our buffers
- let (vbufs, ibuf) = {
- let vbufref: &<back::Backend as hal::Backend>::Buffer = vert_buffer;
-
- let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].into();
- let ibuf = index_buffer;
-
- (vbufs, ibuf)
- };
-
- target.cmd_buffer.begin_primary(CommandBufferFlags::EMPTY);
- // Main render pass / pipeline
- target.cmd_buffer.begin_render_pass(
- renderpass,
- &target.framebuffer,
- self.properties.viewport.rect,
- clear_values.iter(),
- SubpassContents::Inline
- );
- target.cmd_buffer.bind_graphics_pipeline(&pipeline);
-
- // VP Matrix
- let vp = camera.get_matrix().as_slice();
- let vp = std::mem::transmute::<&[f32], &[u32]>(vp);
-
- target.cmd_buffer.push_graphics_constants(
- &pipeline_layout,
- ShaderStageFlags::VERTEX,
- 0,
- vp);
-
- // Bind buffers
- target.cmd_buffer.bind_vertex_buffers(0, vbufs);
- target.cmd_buffer.bind_index_buffer(IndexBufferView {
- buffer: ibuf,
- range: SubRange::WHOLE,
- index_type: hal::IndexType::U16
- });
- };
-
- Ok(&mut target.cmd_buffer)
- }
-
- pub fn finish_and_submit_target(&mut self, command_queue: &mut CommandQueue) -> Result<(), &'static str> {
- let target = &mut self.targets[self.last_drawn];
-
- unsafe {
- target.cmd_buffer.end_render_pass();
- target.cmd_buffer.finish();
- }
-
- // Make submission object
- let command_buffers: std::iter::Once<&CommandBuffer> = once(&target.cmd_buffer);
- let wait_semaphores: std::iter::Once<(&Semaphore, hal::pso::PipelineStage)> = once((&target.get_image, hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT));
- let signal_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete);
-
- let present_wait_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete);
-
- let submission = Submission {
- command_buffers,
- wait_semaphores,
- signal_semaphores,
- };
-
- // Submit it
- unsafe {
- command_queue.submit(submission, Some(&target.present_complete));
- self.swapchain
- .present(command_queue, self.last_image_index as u32, present_wait_semaphores)
- .map_err(|_| "FrameError::PresentError")?;
- };
-
- // TODO
- Ok(())
- }
+ pub fn new(
+ device: &mut Device,
+ adapter: &Adapter,
+ surface: &mut Surface,
+ renderpass: &RenderPass,
+ cmd_pool: &mut CommandPool,
+ properties: SwapchainProperties,
+ old_swapchain: Option<Swapchain>,
+ ) -> Result<TargetChain, TargetChainCreationError> {
+ let caps = surface.capabilities(&adapter.physical_device);
+
+ // Number of frames to pre-render
+ let image_count = if properties.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))
+ };
+
+ // Swap config
+ let swap_config = SwapchainConfig {
+ present_mode: properties.present_mode,
+ composite_alpha_mode: properties.composite_alpha_mode,
+ format: properties.format,
+ extent: Extent2D {
+ width: properties.extent.width,
+ height: properties.extent.height,
+ },
+ image_count,
+ image_layers: 1,
+ image_usage: ImgUsage::COLOR_ATTACHMENT,
+ };
+
+ // Swapchain
+ let (swapchain, mut backbuffer) = unsafe {
+ device
+ .create_swapchain(surface, swap_config, old_swapchain)
+ .map_err(|_| TargetChainCreationError::Todo)?
+ };
+
+ let depth_buffer: LoadedImage = {
+ use hal::format::Aspects;
+ use hal::image::SubresourceRange;
+
+ LoadedImage::new(
+ device,
+ adapter,
+ properties.depth_format,
+ ImgUsage::DEPTH_STENCIL_ATTACHMENT,
+ SubresourceRange {
+ aspects: Aspects::DEPTH,
+ levels: 0..1,
+ layers: 0..1,
+ },
+ properties.extent.width as usize,
+ properties.extent.height as usize,
+ )
+ .map_err(|_| TargetChainCreationError::Todo)
+ }?;
+
+ let mut targets: Vec<TargetResources> = Vec::with_capacity(backbuffer.len());
+ for image in backbuffer.drain(..) {
+ targets.push(
+ TargetResources::new(
+ device,
+ cmd_pool,
+ renderpass,
+ image,
+ &(*depth_buffer.image_view),
+ properties.extent,
+ properties.format,
+ )
+ .map_err(|_| TargetChainCreationError::Todo)?,
+ );
+ }
+
+ Ok(TargetChain {
+ swapchain: ManuallyDrop::new(swapchain),
+ targets: targets.into_boxed_slice(),
+ depth_buffer: ManuallyDrop::new(depth_buffer),
+ properties,
+ last_drawn: (image_count - 1) as usize, // This means the next one to be used is index 0
+ last_image_index: 0,
+ })
+ }
+
+ pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) {
+ use core::ptr::read;
+ unsafe {
+ ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device);
+
+ for i in 0..self.targets.len() {
+ read(&self.targets[i]).deactivate(device, cmd_pool);
+ }
+
+ device.destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain)));
+ }
+ }
+
+ pub fn deactivate_with_recyling(
+ self,
+ device: &mut Device,
+ cmd_pool: &mut CommandPool,
+ ) -> Swapchain {
+ use core::ptr::read;
+ unsafe {
+ ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device);
+
+ for i in 0..self.targets.len() {
+ read(&self.targets[i]).deactivate(device, cmd_pool);
+ }
+ }
+
+ unsafe { ManuallyDrop::into_inner(read(&self.swapchain)) }
+ }
+
+ pub fn prep_next_target<'a>(
+ &'a mut self,
+ device: &mut Device,
+ vert_buffer: &Buffer,
+ index_buffer: &Buffer,
+ renderpass: &RenderPass,
+ pipeline: &GraphicsPipeline,
+ pipeline_layout: &PipelineLayout,
+ camera: &mut WorkingCamera,
+ ) -> Result<&'a mut crate::types::CommandBuffer, &'static str> {
+ self.last_drawn = (self.last_drawn + 1) % self.targets.len();
+
+ let target = &mut self.targets[self.last_drawn];
+
+ // Get the image
+ let (image_index, _) = unsafe {
+ self.swapchain
+ .acquire_image(core::u64::MAX, Some(&target.get_image), None)
+ .map_err(|_| "FrameError::AcquireError")?
+ };
+
+ self.last_image_index = image_index;
+
+ // Make sure whatever was last using this has finished
+ unsafe {
+ device
+ .wait_for_fence(&target.present_complete, core::u64::MAX)
+ .map_err(|_| "FrameError::SyncObjectError")?;
+ device
+ .reset_fence(&target.present_complete)
+ .map_err(|_| "FrameError::SyncObjectError")?;
+ };
+
+ // Record commands
+ unsafe {
+ use hal::buffer::{IndexBufferView, SubRange};
+ use hal::command::{
+ ClearColor, ClearDepthStencil, ClearValue, CommandBufferFlags, SubpassContents,
+ };
+ use hal::pso::ShaderStageFlags;
+
+ // Colour to clear window to
+ let clear_values = [
+ ClearValue {
+ color: ClearColor {
+ float32: [0.0, 0.0, 0.0, 1.0],
+ },
+ },
+ ClearValue {
+ depth_stencil: ClearDepthStencil {
+ depth: 1.0,
+ stencil: 0,
+ },
+ },
+ ];
+
+ // Get references to our buffers
+ let (vbufs, ibuf) = {
+ let vbufref: &<back::Backend as hal::Backend>::Buffer = vert_buffer;
+
+ let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].into();
+ let ibuf = index_buffer;
+
+ (vbufs, ibuf)
+ };
+
+ target.cmd_buffer.begin_primary(CommandBufferFlags::EMPTY);
+ // Main render pass / pipeline
+ target.cmd_buffer.begin_render_pass(
+ renderpass,
+ &target.framebuffer,
+ self.properties.viewport.rect,
+ clear_values.iter(),
+ SubpassContents::Inline,
+ );
+ target.cmd_buffer.bind_graphics_pipeline(&pipeline);
+
+ // VP Matrix
+ let vp = camera.get_matrix().as_slice();
+ let vp = &*(vp as *const [f32] as *const [u32]);
+
+ target.cmd_buffer.push_graphics_constants(
+ &pipeline_layout,
+ ShaderStageFlags::VERTEX,
+ 0,
+ vp,
+ );
+
+ // Bind buffers
+ target.cmd_buffer.bind_vertex_buffers(0, vbufs);
+ target.cmd_buffer.bind_index_buffer(IndexBufferView {
+ buffer: ibuf,
+ range: SubRange::WHOLE,
+ index_type: hal::IndexType::U16,
+ });
+ };
+
+ Ok(&mut target.cmd_buffer)
+ }
+
+ pub fn finish_and_submit_target(
+ &mut self,
+ command_queue: &mut CommandQueue,
+ ) -> Result<(), &'static str> {
+ let target = &mut self.targets[self.last_drawn];
+
+ unsafe {
+ target.cmd_buffer.end_render_pass();
+ target.cmd_buffer.finish();
+ }
+
+ // Make submission object
+ let command_buffers: std::iter::Once<&CommandBuffer> = once(&target.cmd_buffer);
+ let wait_semaphores: std::iter::Once<(&Semaphore, hal::pso::PipelineStage)> = once((
+ &target.get_image,
+ hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT,
+ ));
+ let signal_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete);
+
+ let present_wait_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete);
+
+ let submission = Submission {
+ command_buffers,
+ wait_semaphores,
+ signal_semaphores,
+ };
+
+ // Submit it
+ unsafe {
+ command_queue.submit(submission, Some(&target.present_complete));
+ self.swapchain
+ .present(
+ command_queue,
+ self.last_image_index as u32,
+ present_wait_semaphores,
+ )
+ .map_err(|_| "FrameError::PresentError")?;
+ };
+
+ // TODO
+ Ok(())
+ }
}
/// Resources for a single target frame, including sync objects
pub struct TargetResources {
- /// Command buffer to use when drawing
- pub cmd_buffer: ManuallyDrop<CommandBuffer>,
+ /// Command buffer to use when drawing
+ pub cmd_buffer: ManuallyDrop<CommandBuffer>,
- /// The image for this frame
- pub image: ManuallyDrop<Image>,
+ /// The image for this frame
+ pub image: ManuallyDrop<Image>,
- /// Imageviews for this frame
- pub imageview: ManuallyDrop<ImageView>,
+ /// Imageviews for this frame
+ pub imageview: ManuallyDrop<ImageView>,
- /// Framebuffer for this frame
- pub framebuffer: ManuallyDrop<Framebuffer>,
+ /// Framebuffer for this frame
+ pub framebuffer: ManuallyDrop<Framebuffer>,
- // Sync objects
+ // Sync objects
+ /// Triggered when the image is ready to draw to
+ pub get_image: ManuallyDrop<Semaphore>,
- /// Triggered when the image is ready to draw to
- pub get_image: ManuallyDrop<Semaphore>,
+ /// Triggered when rendering is done
+ pub render_complete: ManuallyDrop<Semaphore>,
- /// Triggered when rendering is done
- pub render_complete: ManuallyDrop<Semaphore>,
-
- /// Triggered when the image is on screen
- pub present_complete: ManuallyDrop<Fence>
+ /// Triggered when the image is on screen
+ pub present_complete: ManuallyDrop<Fence>,
}
-
impl TargetResources {
- pub fn new(device: &mut Device, cmd_pool: &mut CommandPool, renderpass: &RenderPass, image: Image, depth_pass: &ImageView, extent: Extent, format: Format) -> Result<TargetResources, TargetResourcesCreationError> {
- // Command Buffer
- let cmd_buffer = unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) };
-
- // ImageView
- let imageview = unsafe { device.create_image_view(
- &image,
- ViewKind::D2,
- format,
- Swizzle::NO,
- COLOR_RANGE.clone(),
- ).map_err(|e| TargetResourcesCreationError::ImageViewError (e))? };
-
- // Framebuffer
- let framebuffer = unsafe { device.create_framebuffer(
- &renderpass,
- once(&imageview).chain(once(depth_pass)),
- extent
- ).map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)? };
-
- // Sync objects
- let get_image = device.create_semaphore().map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?;
- let render_complete = device.create_semaphore().map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?;
- let present_complete = device.create_fence(true).map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?;
-
- Ok(TargetResources {
- cmd_buffer: ManuallyDrop::new(cmd_buffer),
- image: ManuallyDrop::new(image),
- imageview: ManuallyDrop::new(imageview),
- framebuffer: ManuallyDrop::new(framebuffer),
- get_image: ManuallyDrop::new(get_image),
- render_complete: ManuallyDrop::new(render_complete),
- present_complete: ManuallyDrop::new(present_complete)
- })
- }
-
- pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) -> () {
- use core::ptr::read;
- unsafe {
- cmd_pool.free(once(ManuallyDrop::into_inner(read(&self.cmd_buffer))));
-
- device.destroy_framebuffer(ManuallyDrop::into_inner(read(&self.framebuffer)));
- device.destroy_image_view(ManuallyDrop::into_inner(read(&self.imageview)));
-
- device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.get_image)));
- device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.render_complete)));
- device.destroy_fence(ManuallyDrop::into_inner(read(&self.present_complete)));
- }
- }
+ pub fn new(
+ device: &mut Device,
+ cmd_pool: &mut CommandPool,
+ renderpass: &RenderPass,
+ image: Image,
+ depth_pass: &ImageView,
+ extent: Extent,
+ format: Format,
+ ) -> Result<TargetResources, TargetResourcesCreationError> {
+ // Command Buffer
+ let cmd_buffer = unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) };
+
+ // ImageView
+ let imageview = unsafe {
+ device
+ .create_image_view(
+ &image,
+ ViewKind::D2,
+ format,
+ Swizzle::NO,
+ COLOR_RANGE.clone(),
+ )
+ .map_err(TargetResourcesCreationError::ImageViewError)?
+ };
+
+ // Framebuffer
+ let framebuffer = unsafe {
+ device
+ .create_framebuffer(
+ &renderpass,
+ once(&imageview).chain(once(depth_pass)),
+ extent,
+ )
+ .map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)?
+ };
+
+ // Sync objects
+ let get_image = device
+ .create_semaphore()
+ .map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?;
+ let render_complete = device
+ .create_semaphore()
+ .map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?;
+ let present_complete = device
+ .create_fence(true)
+ .map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?;
+
+ Ok(TargetResources {
+ cmd_buffer: ManuallyDrop::new(cmd_buffer),
+ image: ManuallyDrop::new(image),
+ imageview: ManuallyDrop::new(imageview),
+ framebuffer: ManuallyDrop::new(framebuffer),
+ get_image: ManuallyDrop::new(get_image),
+ render_complete: ManuallyDrop::new(render_complete),
+ present_complete: ManuallyDrop::new(present_complete),
+ })
+ }
+
+ pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) {
+ use core::ptr::read;
+ unsafe {
+ cmd_pool.free(once(ManuallyDrop::into_inner(read(&self.cmd_buffer))));
+
+ device.destroy_framebuffer(ManuallyDrop::into_inner(read(&self.framebuffer)));
+ device.destroy_image_view(ManuallyDrop::into_inner(read(&self.imageview)));
+
+ device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.get_image)));
+ device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.render_complete)));
+ device.destroy_fence(ManuallyDrop::into_inner(read(&self.present_complete)));
+ }
+ }
}
#[derive(Debug)]
pub enum TargetChainCreationError {
- Todo
+ Todo,
}
#[derive(Debug)]
pub enum TargetResourcesCreationError {
- ImageViewError (hal::image::ViewCreationError),
- FrameBufferNoMemory,
- SyncObjectsNoMemory
+ ImageViewError(hal::image::ViewCreationError),
+ FrameBufferNoMemory,
+ SyncObjectsNoMemory,
}
diff --git a/stockton-render/src/draw/texture/chunk.rs b/stockton-render/src/draw/texture/chunk.rs
index 40eb21e..02f2dc5 100644
--- a/stockton-render/src/draw/texture/chunk.rs
+++ b/stockton-render/src/draw/texture/chunk.rs
@@ -16,22 +16,17 @@
//! A chunk of textures is an array of textures, the size of which is known at compile time.
//! This reduces the number of times we need to re-bind our descriptor sets
-use image::{Rgba, RgbaImage};
use hal::prelude::*;
+use image::{Rgba, RgbaImage};
-use core::{
- mem::{replace}
-};
-use std::ops::{Range, Deref};
+use core::mem::replace;
+use std::ops::{Deref, Range};
-use crate::{
- types::*,
- error
-};
+use crate::{error, types::*};
-use log::debug;
-use super::resolver::TextureResolver;
use super::image::SampledImage;
+use super::resolver::TextureResolver;
+use log::debug;
use stockton_levels::prelude::*;
/// The size of a chunk. Needs to match up with the fragment shader
@@ -39,125 +34,135 @@ pub const CHUNK_SIZE: usize = 8;
/// An array of textures
pub struct TextureChunk {
- pub(crate) descriptor_set: DescriptorSet,
- sampled_images: Vec<SampledImage>,
+ pub(crate) descriptor_set: DescriptorSet,
+ sampled_images: Vec<SampledImage>,
}
impl TextureChunk {
- /// Create a new texture chunk and load in the textures specified by `range` from `file` using `resolver`
- /// Can error if the descriptor pool is too small or if a texture isn't found
- pub fn new<T: HasTextures, R: TextureResolver>(device: &mut Device,
- adapter: &mut Adapter,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
- pool: &mut DescriptorPool,
- layout: &DescriptorSetLayout,
- file: &T, range: Range<u32>,
- resolver: &mut R) -> Result<TextureChunk, error::CreationError> {
-
- //
- let descriptor_set = unsafe {
- pool.allocate_set(&layout).map_err(|e| {
- println!("{:?}", e);
- error::CreationError::OutOfMemoryError
- })?
- };
-
- let mut store = TextureChunk {
- descriptor_set: descriptor_set,
- sampled_images: Vec::with_capacity(CHUNK_SIZE),
- };
-
- let mut local_idx = 0;
-
- debug!("Created descriptor set");
- for tex_idx in range {
- debug!("Loading tex {}", local_idx + 1);
- let tex = file.get_texture(tex_idx);
- if let Some(img) = resolver.resolve(tex) {
- store.put_texture(img, local_idx,
- device, adapter,
- command_queue, command_pool).unwrap();
- } else {
- // Texture not found. For now, tear everything down.
- store.deactivate(device);
-
- return Err(error::CreationError::BadDataError);
- }
-
- local_idx += 1;
- }
-
- // Pad out the end if needed
- while local_idx < CHUNK_SIZE {
- debug!("Putting a placeholder in slot {}", local_idx);
- store.put_texture(RgbaImage::from_pixel(1, 1, Rgba ([0, 0, 0, 1])), local_idx,
- device, adapter,
- command_queue, command_pool).unwrap();
-
- local_idx += 1;
- }
-
- Ok(store)
- }
-
-
- pub fn put_texture(&mut self, image: RgbaImage,
- idx: usize,
- device: &mut Device,
- adapter: &mut Adapter,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool) -> Result<(), &'static str>{
-
- // Load the image
- let texture = SampledImage::load_into_new(
- image,
- device,
- adapter,
- command_queue,
- command_pool,
- hal::format::Format::Rgba8Srgb, // TODO
- hal::image::Usage::empty()
- )?;
-
- // Write it to the descriptor set
- unsafe {
- use hal::pso::{DescriptorSetWrite, Descriptor};
- use hal::image::Layout;
-
- device.write_descriptor_sets(vec![
- DescriptorSetWrite {
- set: &self.descriptor_set,
- binding: 0,
- array_offset: idx,
- descriptors: Some(Descriptor::Image(
- texture.image.image_view.deref(),
- Layout::ShaderReadOnlyOptimal
- )),
- },
- DescriptorSetWrite {
- set: &self.descriptor_set,
- binding: 1,
- array_offset: idx,
- descriptors: Some(Descriptor::Sampler(texture.sampler.deref())),
- },
- ]);
- };
-
- // Store it so we can safely deactivate it when we need to
- // Deactivate the old image if we need to
- if idx < self.sampled_images.len() {
- replace(&mut self.sampled_images[idx], texture).deactivate(device);
- } else {
- self.sampled_images.push(texture);
- }
-
- Ok(())
- }
-
- pub fn deactivate(mut self, device: &mut Device) -> () {
- for img in self.sampled_images.drain(..) {
- img.deactivate(device);
- }
- }
-} \ No newline at end of file
+ /// Create a new texture chunk and load in the textures specified by `range` from `file` using `resolver`
+ /// Can error if the descriptor pool is too small or if a texture isn't found
+ pub fn new<T: HasTextures, R: TextureResolver>(
+ device: &mut Device,
+ adapter: &mut Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ pool: &mut DescriptorPool,
+ layout: &DescriptorSetLayout,
+ file: &T,
+ range: Range<u32>,
+ resolver: &mut R,
+ ) -> Result<TextureChunk, error::CreationError> {
+ //
+ let descriptor_set = unsafe {
+ pool.allocate_set(&layout).map_err(|e| {
+ println!("{:?}", e);
+ error::CreationError::OutOfMemoryError
+ })?
+ };
+
+ let mut store = TextureChunk {
+ descriptor_set,
+ sampled_images: Vec::with_capacity(CHUNK_SIZE),
+ };
+
+ let mut local_idx = 0;
+
+ debug!("Created descriptor set");
+ for tex_idx in range {
+ debug!("Loading tex {}", local_idx + 1);
+ let tex = file.get_texture(tex_idx);
+ if let Some(img) = resolver.resolve(tex) {
+ store
+ .put_texture(img, local_idx, device, adapter, command_queue, command_pool)
+ .unwrap();
+ } else {
+ // Texture not found. For now, tear everything down.
+ store.deactivate(device);
+
+ return Err(error::CreationError::BadDataError);
+ }
+
+ local_idx += 1;
+ }
+
+ // Pad out the end if needed
+ while local_idx < CHUNK_SIZE {
+ debug!("Putting a placeholder in slot {}", local_idx);
+ store
+ .put_texture(
+ RgbaImage::from_pixel(1, 1, Rgba([0, 0, 0, 1])),
+ local_idx,
+ device,
+ adapter,
+ command_queue,
+ command_pool,
+ )
+ .unwrap();
+
+ local_idx += 1;
+ }
+
+ Ok(store)
+ }
+
+ pub fn put_texture(
+ &mut self,
+ image: RgbaImage,
+ idx: usize,
+ device: &mut Device,
+ adapter: &mut Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ ) -> Result<(), &'static str> {
+ // Load the image
+ let texture = SampledImage::load_into_new(
+ image,
+ device,
+ adapter,
+ command_queue,
+ command_pool,
+ hal::format::Format::Rgba8Srgb, // TODO
+ hal::image::Usage::empty(),
+ )?;
+
+ // Write it to the descriptor set
+ unsafe {
+ use hal::image::Layout;
+ use hal::pso::{Descriptor, DescriptorSetWrite};
+
+ device.write_descriptor_sets(vec![
+ DescriptorSetWrite {
+ set: &self.descriptor_set,
+ binding: 0,
+ array_offset: idx,
+ descriptors: Some(Descriptor::Image(
+ texture.image.image_view.deref(),
+ Layout::ShaderReadOnlyOptimal,
+ )),
+ },
+ DescriptorSetWrite {
+ set: &self.descriptor_set,
+ binding: 1,
+ array_offset: idx,
+ descriptors: Some(Descriptor::Sampler(texture.sampler.deref())),
+ },
+ ]);
+ };
+
+ // Store it so we can safely deactivate it when we need to
+ // Deactivate the old image if we need to
+ if idx < self.sampled_images.len() {
+ replace(&mut self.sampled_images[idx], texture).deactivate(device);
+ } else {
+ self.sampled_images.push(texture);
+ }
+
+ Ok(())
+ }
+
+ pub fn deactivate(mut self, device: &mut Device) {
+ for img in self.sampled_images.drain(..) {
+ img.deactivate(device);
+ }
+ }
+}
diff --git a/stockton-render/src/draw/texture/image.rs b/stockton-render/src/draw/texture/image.rs
index 530628a..0b8c64e 100644
--- a/stockton-render/src/draw/texture/image.rs
+++ b/stockton-render/src/draw/texture/image.rs
@@ -14,23 +14,20 @@
// with this program. If not, see <http://www.gnu.org/licenses/>.
use core::{
- ptr::copy_nonoverlapping,
- mem::{size_of, ManuallyDrop}
+ mem::{size_of, ManuallyDrop},
+ ptr::copy_nonoverlapping,
};
-use std::{
- iter::once,
- convert::TryInto
-};
-use image::RgbaImage;
use hal::{
- MemoryTypeId,
- buffer::Usage as BufUsage,
- format::{Format, Swizzle, Aspects},
- image::{ViewKind, SubresourceRange, Usage as ImgUsage},
- queue::Submission,
- memory::{Properties as MemProperties, Dependencies as MemDependencies, Segment},
- prelude::*,
+ buffer::Usage as BufUsage,
+ format::{Aspects, Format, Swizzle},
+ image::{SubresourceRange, Usage as ImgUsage, ViewKind},
+ memory::{Dependencies as MemDependencies, Properties as MemProperties, Segment},
+ prelude::*,
+ queue::Submission,
+ MemoryTypeId,
};
+use image::RgbaImage;
+use std::{convert::TryInto, iter::once};
use crate::types::*;
use draw::buffer::create_buffer;
@@ -40,297 +37,367 @@ const PIXEL_SIZE: usize = size_of::<image::Rgba<u8>>();
/// Holds an image that's loaded into GPU memory and can be sampled from
pub struct LoadedImage {
- /// The GPU Image handle
- image: ManuallyDrop<Image>,
+ /// The GPU Image handle
+ image: ManuallyDrop<Image>,
- /// The full view of the image
- pub image_view: ManuallyDrop<ImageView>,
+ /// The full view of the image
+ pub image_view: ManuallyDrop<ImageView>,
- /// The memory backing the image
- memory: ManuallyDrop<Memory>
+ /// The memory backing the image
+ memory: ManuallyDrop<Memory>,
}
-pub fn create_image_view(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, width: usize, height: usize) -> Result<(Memory, Image), &'static str> {
- // Round up the size to align properly
- let initial_row_size = PIXEL_SIZE * width;
- let limits = adapter.physical_device.limits();
- let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1;
-
- let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
- debug_assert!(row_size as usize >= initial_row_size);
-
- // Make the image
- let mut image_ref = unsafe {
- use hal::image::{Kind, Tiling, ViewCapabilities};
-
- device.create_image(
- Kind::D2(width as u32, height as u32, 1, 1),
- 1,
- format,
- Tiling::Optimal,
- usage,
- ViewCapabilities::empty()
- )
- }.map_err(|_| "Couldn't create image")?;
-
- // Allocate memory
- let memory = unsafe {
- let requirements = device.get_image_requirements(&image_ref);
-
- let memory_type_id = adapter.physical_device
- .memory_properties().memory_types
- .iter().enumerate()
- .find(|&(id, memory_type)| {
- requirements.type_mask & (1 << id) != 0 && memory_type.properties.contains(MemProperties::DEVICE_LOCAL)
- })
- .map(|(id, _)| MemoryTypeId(id))
- .ok_or("Couldn't find a memory type for image memory")?;
-
- let memory = device
- .allocate_memory(memory_type_id, requirements.size)
- .map_err(|_| "Couldn't allocate image memory")?;
-
- device.bind_image_memory(&memory, 0, &mut image_ref)
- .map_err(|_| "Couldn't bind memory to image")?;
-
- Ok(memory)
- }?;
-
- Ok((memory, image_ref))
-
+pub fn create_image_view(
+ device: &mut Device,
+ adapter: &Adapter,
+ format: Format,
+ usage: ImgUsage,
+ width: usize,
+ height: usize,
+) -> Result<(Memory, Image), &'static str> {
+ // Round up the size to align properly
+ let initial_row_size = PIXEL_SIZE * width;
+ let limits = adapter.physical_device.limits();
+ let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1;
+
+ let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
+ debug_assert!(row_size as usize >= initial_row_size);
+
+ // Make the image
+ let mut image_ref = unsafe {
+ use hal::image::{Kind, Tiling, ViewCapabilities};
+
+ device.create_image(
+ Kind::D2(width as u32, height as u32, 1, 1),
+ 1,
+ format,
+ Tiling::Optimal,
+ usage,
+ ViewCapabilities::empty(),
+ )
+ }
+ .map_err(|_| "Couldn't create image")?;
+
+ // Allocate memory
+ let memory = unsafe {
+ let requirements = device.get_image_requirements(&image_ref);
+
+ let memory_type_id = adapter
+ .physical_device
+ .memory_properties()
+ .memory_types
+ .iter()
+ .enumerate()
+ .find(|&(id, memory_type)| {
+ requirements.type_mask & (1 << id) != 0
+ && memory_type.properties.contains(MemProperties::DEVICE_LOCAL)
+ })
+ .map(|(id, _)| MemoryTypeId(id))
+ .ok_or("Couldn't find a memory type for image memory")?;
+
+ let memory = device
+ .allocate_memory(memory_type_id, requirements.size)
+ .map_err(|_| "Couldn't allocate image memory")?;
+
+ device
+ .bind_image_memory(&memory, 0, &mut image_ref)
+ .map_err(|_| "Couldn't bind memory to image")?;
+
+ Ok(memory)
+ }?;
+
+ Ok((memory, image_ref))
}
impl LoadedImage {
- pub fn new(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, resources: SubresourceRange, width: usize, height: usize) -> Result<LoadedImage, &'static str> {
- let (memory, image_ref) = create_image_view(device, adapter, format, usage, width, height)?;
-
- // Create ImageView and sampler
- let image_view = unsafe { device.create_image_view(
- &image_ref,
- ViewKind::D2,
- format,
- Swizzle::NO,
- resources,
- )}.map_err(|_| "Couldn't create the image view!")?;
-
- Ok(LoadedImage {
- image: ManuallyDrop::new(image_ref),
- image_view: ManuallyDrop::new(image_view),
- memory: ManuallyDrop::new(memory)
- })
- }
-
- /// Load the given image
- pub fn load(&mut self, img: RgbaImage, device: &mut Device, adapter: &Adapter, command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool) -> Result<(), &'static str> {
- let initial_row_size = PIXEL_SIZE * img.width() as usize;
- let limits = adapter.physical_device.limits();
- let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1;
-
- let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
- let total_size = (row_size * (img.height() as usize)) as u64;
- debug_assert!(row_size as usize >= initial_row_size);
-
- // Make a staging buffer
- let (staging_buffer, staging_memory) = create_buffer(device, adapter, BufUsage::TRANSFER_SRC, MemProperties::CPU_VISIBLE, total_size)
- .map_err(|_| "Couldn't create staging buffer")?;
-
- // Copy everything into it
- unsafe {
- let mapped_memory: *mut u8 = device.map_memory(&staging_memory, Segment::ALL).map_err(|_| "Couldn't map buffer memory")?;
-
- for y in 0..img.height() as usize {
- let row = &(*img)[y * initial_row_size..(y + 1) * initial_row_size];
- let dest_base: isize = (y * row_size).try_into().unwrap();
-
- copy_nonoverlapping(row.as_ptr(), mapped_memory.offset(dest_base), row.len());
- }
- device.flush_mapped_memory_ranges(once((&staging_memory, Segment::ALL))).map_err(|_| "Couldn't write buffer memory")?;
- device.unmap_memory(&staging_memory);
- }
-
- // Copy from staging to image memory
- let buf = unsafe {
- use hal::command::{CommandBufferFlags, BufferImageCopy};
- use hal::pso::PipelineStage;
- use hal::memory::Barrier;
- use hal::image::{Access, Layout, SubresourceLayers, Offset, Extent};
-
- // Get a command buffer
- let mut buf = command_pool.allocate_one(hal::command::Level::Primary);
- buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT);
-
- // Setup the layout of our image for copying
- let image_barrier = Barrier::Image {
- states: (Access::empty(), Layout::Undefined)
- ..(
- Access::TRANSFER_WRITE,
- Layout::TransferDstOptimal,
- ),
- target: &(*self.image),
- families: None,
- range: SubresourceRange {
- aspects: Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
- },
- };
- buf.pipeline_barrier(
- PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER,
- MemDependencies::empty(),
- &[image_barrier],
- );
-
- // Copy from buffer to image
- buf.copy_buffer_to_image(&staging_buffer, &(*self.image),
- Layout::TransferDstOptimal, &[
- BufferImageCopy {
- buffer_offset: 0,
- buffer_width: (row_size / PIXEL_SIZE) as u32,
- buffer_height: img.height(),
- image_layers: SubresourceLayers {
- aspects: Aspects::COLOR,
- level: 0,
- layers: 0..1
- },
- image_offset: Offset {
- x: 0, y: 0, z: 0
- },
- image_extent: Extent {
- width: img.width(),
- height: img.height(),
- depth: 1
- }
- }
- ]);
-
- // Setup the layout of our image for shaders
- let image_barrier = Barrier::Image {
- states: (
- Access::TRANSFER_WRITE,
- Layout::TransferDstOptimal,
- )..(
- Access::SHADER_READ,
- Layout::ShaderReadOnlyOptimal,
- ),
- target: &(*self.image),
- families: None,
- range: SubresourceRange {
- aspects: Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
- },
- };
-
- buf.pipeline_barrier(
- PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER,
- MemDependencies::empty(),
- &[image_barrier],
- );
-
- buf.finish();
-
- buf
- };
-
- // Submit our commands and wait for them to finish
- unsafe {
- let setup_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(&setup_finished));
-
- device
- .wait_for_fence(&setup_finished, core::u64::MAX).unwrap();
- device.destroy_fence(setup_finished);
- };
-
- // Clean up temp resources
- unsafe {
- command_pool.free(once(buf));
-
- device.free_memory(staging_memory);
- device.destroy_buffer(staging_buffer);
- }
-
- Ok(())
- }
-
- /// Load the given image into a new buffer
- pub fn load_into_new(img: RgbaImage, device: &mut Device, adapter: &Adapter,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool, format: Format, usage: ImgUsage) -> Result<LoadedImage, &'static str> {
- let mut loaded_image = Self::new(device, adapter, format, usage | ImgUsage::TRANSFER_DST, SubresourceRange {
- aspects: Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
- }, img.width() as usize, img.height() as usize)?;
- loaded_image.load(img, device, adapter, command_queue, command_pool)?;
-
- Ok(loaded_image)
- }
-
- /// Properly frees/destroys all the objects in this struct
- /// Dropping without doing this is a bad idea
- pub fn deactivate(self, device: &Device) -> () {
- unsafe {
- use core::ptr::read;
-
- device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view)));
- device.destroy_image(ManuallyDrop::into_inner(read(&self.image)));
- device.free_memory(ManuallyDrop::into_inner(read(&self.memory)));
- }
- }
+ pub fn new(
+ device: &mut Device,
+ adapter: &Adapter,
+ format: Format,
+ usage: ImgUsage,
+ resources: SubresourceRange,
+ width: usize,
+ height: usize,
+ ) -> Result<LoadedImage, &'static str> {
+ let (memory, image_ref) = create_image_view(device, adapter, format, usage, width, height)?;
+
+ // Create ImageView and sampler
+ let image_view = unsafe {
+ device.create_image_view(&image_ref, ViewKind::D2, format, Swizzle::NO, resources)
+ }
+ .map_err(|_| "Couldn't create the image view!")?;
+
+ Ok(LoadedImage {
+ image: ManuallyDrop::new(image_ref),
+ image_view: ManuallyDrop::new(image_view),
+ memory: ManuallyDrop::new(memory),
+ })
+ }
+
+ /// Load the given image
+ pub fn load(
+ &mut self,
+ img: RgbaImage,
+ device: &mut Device,
+ adapter: &Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ ) -> Result<(), &'static str> {
+ let initial_row_size = PIXEL_SIZE * img.width() as usize;
+ let limits = adapter.physical_device.limits();
+ let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1;
+
+ let row_size =
+ ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
+ let total_size = (row_size * (img.height() as usize)) as u64;
+ debug_assert!(row_size as usize >= initial_row_size);
+
+ // Make a staging buffer
+ let (staging_buffer, staging_memory) = create_buffer(
+ device,
+ adapter,
+ BufUsage::TRANSFER_SRC,
+ MemProperties::CPU_VISIBLE,
+ total_size,
+ )
+ .map_err(|_| "Couldn't create staging buffer")?;
+
+ // Copy everything into it
+ unsafe {
+ let mapped_memory: *mut u8 = device
+ .map_memory(&staging_memory, Segment::ALL)
+ .map_err(|_| "Couldn't map buffer memory")?;
+
+ for y in 0..img.height() as usize {
+ let row = &(*img)[y * initial_row_size..(y + 1) * initial_row_size];
+ let dest_base: isize = (y * row_size).try_into().unwrap();
+
+ copy_nonoverlapping(row.as_ptr(), mapped_memory.offset(dest_base), row.len());
+ }
+ device
+ .flush_mapped_memory_ranges(once((&staging_memory, Segment::ALL)))
+ .map_err(|_| "Couldn't write buffer memory")?;
+ device.unmap_memory(&staging_memory);
+ }
+
+ // Copy from staging to image memory
+ let buf = unsafe {
+ use hal::command::{BufferImageCopy, CommandBufferFlags};
+ use hal::image::{Access, Extent, Layout, Offset, SubresourceLayers};
+ use hal::memory::Barrier;
+ use hal::pso::PipelineStage;
+
+ // Get a command buffer
+ let mut buf = command_pool.allocate_one(hal::command::Level::Primary);
+ buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT);
+
+ // Setup the layout of our image for copying
+ let image_barrier = Barrier::Image {
+ states: (Access::empty(), Layout::Undefined)
+ ..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal),
+ target: &(*self.image),
+ families: None,
+ range: SubresourceRange {
+ aspects: Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
+ },
+ };
+ buf.pipeline_barrier(
+ PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER,
+ MemDependencies::empty(),
+ &[image_barrier],
+ );
+
+ // Copy from buffer to image
+ buf.copy_buffer_to_image(
+ &staging_buffer,
+ &(*self.image),
+ Layout::TransferDstOptimal,
+ &[BufferImageCopy {
+ buffer_offset: 0,
+ buffer_width: (row_size / PIXEL_SIZE) as u32,
+ buffer_height: img.height(),
+ image_layers: SubresourceLayers {
+ aspects: Aspects::COLOR,
+ level: 0,
+ layers: 0..1,
+ },
+ image_offset: Offset { x: 0, y: 0, z: 0 },
+ image_extent: Extent {
+ width: img.width(),
+ height: img.height(),
+ depth: 1,
+ },
+ }],
+ );
+
+ // Setup the layout of our image for shaders
+ let image_barrier = Barrier::Image {
+ states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal)
+ ..(Access::SHADER_READ, Layout::ShaderReadOnlyOptimal),
+ target: &(*self.image),
+ families: None,
+ range: SubresourceRange {
+ aspects: Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
+ },
+ };
+
+ buf.pipeline_barrier(
+ PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER,
+ MemDependencies::empty(),
+ &[image_barrier],
+ );
+
+ buf.finish();
+
+ buf
+ };
+
+ // Submit our commands and wait for them to finish
+ unsafe {
+ let setup_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(&setup_finished),
+ );
+
+ device
+ .wait_for_fence(&setup_finished, core::u64::MAX)
+ .unwrap();
+ device.destroy_fence(setup_finished);
+ };
+
+ // Clean up temp resources
+ unsafe {
+ command_pool.free(once(buf));
+
+ device.free_memory(staging_memory);
+ device.destroy_buffer(staging_buffer);
+ }
+
+ Ok(())
+ }
+
+ /// Load the given image into a new buffer
+ pub fn load_into_new(
+ img: RgbaImage,
+ device: &mut Device,
+ adapter: &Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ format: Format,
+ usage: ImgUsage,
+ ) -> Result<LoadedImage, &'static str> {
+ let mut loaded_image = Self::new(
+ device,
+ adapter,
+ format,
+ usage | ImgUsage::TRANSFER_DST,
+ SubresourceRange {
+ aspects: Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
+ },
+ img.width() as usize,
+ img.height() as usize,
+ )?;
+ loaded_image.load(img, device, adapter, command_queue, command_pool)?;
+
+ Ok(loaded_image)
+ }
+
+ /// Properly frees/destroys all the objects in this struct
+ /// Dropping without doing this is a bad idea
+ pub fn deactivate(self, device: &Device) {
+ unsafe {
+ use core::ptr::read;
+
+ device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view)));
+ device.destroy_image(ManuallyDrop::into_inner(read(&self.image)));
+ device.free_memory(ManuallyDrop::into_inner(read(&self.memory)));
+ }
+ }
}
-
pub struct SampledImage {
- pub image: ManuallyDrop<LoadedImage>,
- pub sampler: ManuallyDrop<Sampler>
+ pub image: ManuallyDrop<LoadedImage>,
+ pub sampler: ManuallyDrop<Sampler>,
}
impl SampledImage {
- pub fn new(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, width: usize, height: usize) -> Result<SampledImage, &'static str> {
- let image = LoadedImage::new(device, adapter, format, usage | ImgUsage::SAMPLED, SubresourceRange {
- aspects: Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
- }, width, height)?;
-
- let sampler = unsafe {
- use hal::image::{SamplerDesc, Filter, WrapMode};
-
- device.create_sampler(&SamplerDesc::new(
- Filter::Nearest,
- WrapMode::Tile,
- ))
- }.map_err(|_| "Couldn't create the sampler!")?;
-
- Ok(SampledImage {
- image: ManuallyDrop::new(image),
- sampler: ManuallyDrop::new(sampler)
- })
- }
-
- pub fn load_into_new(img: RgbaImage, device: &mut Device, adapter: &Adapter,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool, format: Format, usage: ImgUsage) -> Result<SampledImage, &'static str> {
- let mut sampled_image = SampledImage::new(device, adapter, format, usage | ImgUsage::TRANSFER_DST, img.width() as usize, img.height() as usize)?;
- sampled_image.image.load(img, device, adapter, command_queue, command_pool)?;
-
- Ok(sampled_image)
-
- }
-
- pub fn deactivate(self, device: &mut Device) -> () {
- unsafe {
- use core::ptr::read;
-
- device.destroy_sampler(ManuallyDrop::into_inner(read(&self.sampler)));
-
- ManuallyDrop::into_inner(read(&self.image)).deactivate(device);
- }
- }
+ pub fn new(
+ device: &mut Device,
+ adapter: &Adapter,
+ format: Format,
+ usage: ImgUsage,
+ width: usize,
+ height: usize,
+ ) -> Result<SampledImage, &'static str> {
+ let image = LoadedImage::new(
+ device,
+ adapter,
+ format,
+ usage | ImgUsage::SAMPLED,
+ SubresourceRange {
+ aspects: Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
+ },
+ width,
+ height,
+ )?;
+
+ let sampler = unsafe {
+ use hal::image::{Filter, SamplerDesc, WrapMode};
+
+ device.create_sampler(&SamplerDesc::new(Filter::Nearest, WrapMode::Tile))
+ }
+ .map_err(|_| "Couldn't create the sampler!")?;
+
+ Ok(SampledImage {
+ image: ManuallyDrop::new(image),
+ sampler: ManuallyDrop::new(sampler),
+ })
+ }
+
+ pub fn load_into_new(
+ img: RgbaImage,
+ device: &mut Device,
+ adapter: &Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ format: Format,
+ usage: ImgUsage,
+ ) -> Result<SampledImage, &'static str> {
+ let mut sampled_image = SampledImage::new(
+ device,
+ adapter,
+ format,
+ usage | ImgUsage::TRANSFER_DST,
+ img.width() as usize,
+ img.height() as usize,
+ )?;
+ sampled_image
+ .image
+ .load(img, device, adapter, command_queue, command_pool)?;
+
+ Ok(sampled_image)
+ }
+
+ pub fn deactivate(self, device: &mut Device) {
+ unsafe {
+ use core::ptr::read;
+
+ device.destroy_sampler(ManuallyDrop::into_inner(read(&self.sampler)));
+
+ ManuallyDrop::into_inner(read(&self.image)).deactivate(device);
+ }
+ }
}
diff --git a/stockton-render/src/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs
index 1ac0961..863e1a8 100644
--- a/stockton-render/src/draw/texture/loader.rs
+++ b/stockton-render/src/draw/texture/loader.rs
@@ -14,17 +14,15 @@
// with this program. If not, see <http://www.gnu.org/licenses/>.
//! Deals with loading textures into GPU memory
-use std::path::Path;
-use draw::texture::resolver::BasicFSResolver;
-use draw::texture::chunk::CHUNK_SIZE;
-use core::mem::{ManuallyDrop};
use super::chunk::TextureChunk;
+use core::mem::ManuallyDrop;
+use draw::texture::chunk::CHUNK_SIZE;
+use draw::texture::resolver::BasicFSResolver;
+use std::path::Path;
use log::debug;
-use hal::{
- prelude::*,
-};
+use hal::prelude::*;
use stockton_levels::prelude::*;
@@ -36,137 +34,151 @@ use crate::types::*;
/// The descriptor set layout should have the same count of textures as this does.
/// All descriptors will be properly initialised images.
pub struct TextureStore {
- descriptor_pool: ManuallyDrop<DescriptorPool>,
- pub(crate) descriptor_set_layout: ManuallyDrop<DescriptorSetLayout>,
- chunks: Box<[TextureChunk]>
+ descriptor_pool: ManuallyDrop<DescriptorPool>,
+ pub(crate) descriptor_set_layout: ManuallyDrop<DescriptorSetLayout>,
+ chunks: Box<[TextureChunk]>,
}
impl TextureStore {
- /// Create a new texture store for the given file, loading all textures from it.
- pub fn new<T: HasTextures>(device: &mut Device,
- adapter: &mut Adapter,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool, file: &T) -> Result<TextureStore, error::CreationError> {
- // Figure out how many textures in this file / how many chunks needed
- let size = file.textures_iter().count();
- let num_chunks = {
- let mut x = size / CHUNK_SIZE;
- if size % CHUNK_SIZE != 0 {
- x += 1;
- }
- x
- };
- let rounded_size = num_chunks * CHUNK_SIZE;
-
- // Descriptor pool, where we get our sets from
- let mut descriptor_pool = unsafe {
- use hal::pso::{DescriptorRangeDesc, DescriptorType, DescriptorPoolCreateFlags, ImageDescriptorType};
-
- device.create_descriptor_pool(
- num_chunks,
- &[
- DescriptorRangeDesc {
- ty: DescriptorType::Image {
- ty: ImageDescriptorType::Sampled {
- with_sampler: false
- }
- },
- count: rounded_size
- },
- DescriptorRangeDesc {
- ty: DescriptorType::Sampler,
- count: rounded_size
- }
- ],
- DescriptorPoolCreateFlags::empty()
- ).map_err(|e| {
- println!("{:?}", e);
- error::CreationError::OutOfMemoryError
- })?
- };
-
- // Layout of our descriptor sets
- let mut descriptor_set_layout = unsafe {
- use hal::pso::{DescriptorSetLayoutBinding, DescriptorType, ShaderStageFlags, ImageDescriptorType};
-
- device.create_descriptor_set_layout(
- &[
- DescriptorSetLayoutBinding {
- binding: 0,
- ty: DescriptorType::Image {
- ty: ImageDescriptorType::Sampled {
- with_sampler: false
- }
- },
- count: CHUNK_SIZE,
- stage_flags: ShaderStageFlags::FRAGMENT,
- immutable_samplers: false
- },
- DescriptorSetLayoutBinding {
- binding: 1,
- ty: DescriptorType::Sampler,
- count: CHUNK_SIZE,
- stage_flags: ShaderStageFlags::FRAGMENT,
- immutable_samplers: false
- }
- ],
- &[],
- )
- }.map_err(|_| error::CreationError::OutOfMemoryError)?;
-
- // TODO: Proper way to set up resolver
- let mut resolver = BasicFSResolver::new(Path::new("."));
-
- // Create texture chunks
- debug!("Starting to load textures...");
- let mut chunks = Vec::with_capacity(num_chunks);
- for i in 0..num_chunks {
- let range = {
- let mut r = (i * CHUNK_SIZE) as u32..((i + 1) * CHUNK_SIZE) as u32;
- if r.end > size as u32 {
- r.end = size as u32;
- }
- r
- };
- debug!("Chunk {} / {} covering {:?}", i + 1, num_chunks, range);
-
- chunks.push(
- TextureChunk::new(
- device, adapter, command_queue,
- command_pool, &mut descriptor_pool,
- &mut descriptor_set_layout, file,
- range, &mut resolver
- )?
- );
- }
-
- debug!("All textures loaded.");
-
- Ok(TextureStore {
- descriptor_pool: ManuallyDrop::new(descriptor_pool),
- descriptor_set_layout: ManuallyDrop::new(descriptor_set_layout),
- chunks: chunks.into_boxed_slice()
- })
- }
-
- /// Call this before dropping
- pub fn deactivate(mut self, device: &mut Device) -> () {
- unsafe {
- use core::ptr::read;
-
- for chunk in self.chunks.into_vec().drain(..) {
- chunk.deactivate(device)
- }
-
- self.descriptor_pool.reset();
- device
- .destroy_descriptor_set_layout(ManuallyDrop::into_inner(read(&self.descriptor_set_layout)));
- device.destroy_descriptor_pool(ManuallyDrop::into_inner(read(&self.descriptor_pool)));
- }
- }
-
- /// Get the descriptor set for a given chunk
- pub fn get_chunk_descriptor_set<'a>(&'a self, idx: usize) -> &'a DescriptorSet {
- &self.chunks[idx].descriptor_set
- }
+ /// Create a new texture store for the given file, loading all textures from it.
+ pub fn new<T: HasTextures>(
+ device: &mut Device,
+ adapter: &mut Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ file: &T,
+ ) -> Result<TextureStore, error::CreationError> {
+ // Figure out how many textures in this file / how many chunks needed
+ let size = file.textures_iter().count();
+ let num_chunks = {
+ let mut x = size / CHUNK_SIZE;
+ if size % CHUNK_SIZE != 0 {
+ x += 1;
+ }
+ x
+ };
+ let rounded_size = num_chunks * CHUNK_SIZE;
+
+ // Descriptor pool, where we get our sets from
+ let mut descriptor_pool = unsafe {
+ use hal::pso::{
+ DescriptorPoolCreateFlags, DescriptorRangeDesc, DescriptorType, ImageDescriptorType,
+ };
+
+ device
+ .create_descriptor_pool(
+ num_chunks,
+ &[
+ DescriptorRangeDesc {
+ ty: DescriptorType::Image {
+ ty: ImageDescriptorType::Sampled {
+ with_sampler: false,
+ },
+ },
+ count: rounded_size,
+ },
+ DescriptorRangeDesc {
+ ty: DescriptorType::Sampler,
+ count: rounded_size,
+ },
+ ],
+ DescriptorPoolCreateFlags::empty(),
+ )
+ .map_err(|e| {
+ println!("{:?}", e);
+ error::CreationError::OutOfMemoryError
+ })?
+ };
+
+ // Layout of our descriptor sets
+ let descriptor_set_layout = unsafe {
+ use hal::pso::{
+ DescriptorSetLayoutBinding, DescriptorType, ImageDescriptorType, ShaderStageFlags,
+ };
+
+ device.create_descriptor_set_layout(
+ &[
+ DescriptorSetLayoutBinding {
+ binding: 0,
+ ty: DescriptorType::Image {
+ ty: ImageDescriptorType::Sampled {
+ with_sampler: false,
+ },
+ },
+ count: CHUNK_SIZE,
+ stage_flags: ShaderStageFlags::FRAGMENT,
+ immutable_samplers: false,
+ },
+ DescriptorSetLayoutBinding {
+ binding: 1,
+ ty: DescriptorType::Sampler,
+ count: CHUNK_SIZE,
+ stage_flags: ShaderStageFlags::FRAGMENT,
+ immutable_samplers: false,
+ },
+ ],
+ &[],
+ )
+ }
+ .map_err(|_| error::CreationError::OutOfMemoryError)?;
+
+ // TODO: Proper way to set up resolver
+ let mut resolver = BasicFSResolver::new(Path::new("."));
+
+ // Create texture chunks
+ debug!("Starting to load textures...");
+ let mut chunks = Vec::with_capacity(num_chunks);
+ for i in 0..num_chunks {
+ let range = {
+ let mut r = (i * CHUNK_SIZE) as u32..((i + 1) * CHUNK_SIZE) as u32;
+ if r.end > size as u32 {
+ r.end = size as u32;
+ }
+ r
+ };
+ debug!("Chunk {} / {} covering {:?}", i + 1, num_chunks, range);
+
+ chunks.push(TextureChunk::new(
+ device,
+ adapter,
+ command_queue,
+ command_pool,
+ &mut descriptor_pool,
+ &descriptor_set_layout,
+ file,
+ range,
+ &mut resolver,
+ )?);
+ }
+
+ debug!("All textures loaded.");
+
+ Ok(TextureStore {
+ descriptor_pool: ManuallyDrop::new(descriptor_pool),
+ descriptor_set_layout: ManuallyDrop::new(descriptor_set_layout),
+ chunks: chunks.into_boxed_slice(),
+ })
+ }
+
+ /// Call this before dropping
+ pub fn deactivate(mut self, device: &mut Device) {
+ unsafe {
+ use core::ptr::read;
+
+ for chunk in self.chunks.into_vec().drain(..) {
+ chunk.deactivate(device)
+ }
+
+ self.descriptor_pool.reset();
+ device.destroy_descriptor_set_layout(ManuallyDrop::into_inner(read(
+ &self.descriptor_set_layout,
+ )));
+ device.destroy_descriptor_pool(ManuallyDrop::into_inner(read(&self.descriptor_pool)));
+ }
+ }
+
+ /// Get the descriptor set for a given chunk
+ pub fn get_chunk_descriptor_set(&self, idx: usize) -> &DescriptorSet {
+ &self.chunks[idx].descriptor_set
+ }
}
diff --git a/stockton-render/src/draw/texture/mod.rs b/stockton-render/src/draw/texture/mod.rs
index 05dfe38..756fe79 100644
--- a/stockton-render/src/draw/texture/mod.rs
+++ b/stockton-render/src/draw/texture/mod.rs
@@ -15,10 +15,10 @@
//! Everything related to loading textures into GPU memory
-mod resolver;
-pub mod image;
mod chunk;
+pub mod image;
pub mod loader;
+mod resolver;
+pub use self::image::{LoadedImage, SampledImage};
pub use self::loader::TextureStore;
-pub use self::image::{LoadedImage, SampledImage}; \ No newline at end of file
diff --git a/stockton-render/src/draw/texture/resolver.rs b/stockton-render/src/draw/texture/resolver.rs
index 5867171..5fab16a 100644
--- a/stockton-render/src/draw/texture/resolver.rs
+++ b/stockton-render/src/draw/texture/resolver.rs
@@ -17,44 +17,39 @@
use stockton_levels::traits::textures::Texture;
-use image::{
- RgbaImage,
- io::Reader
-};
+use image::{io::Reader, RgbaImage};
use std::path::Path;
/// An object that can be used to resolve a texture from a BSP File
pub trait TextureResolver {
- /// Get the given texture, or None if it's corrupt/not there.
- fn resolve(&mut self, texture: &Texture) -> Option<RgbaImage>;
+ /// Get the given texture, or None if it's corrupt/not there.
+ fn resolve(&mut self, texture: &Texture) -> Option<RgbaImage>;
}
/// A basic filesystem resolver which expects no file extension and guesses the image format
pub struct BasicFSResolver<'a> {
- path: &'a Path
+ path: &'a Path,
}
impl<'a> BasicFSResolver<'a> {
- pub fn new(path: &'a Path) -> BasicFSResolver<'a> {
- BasicFSResolver {
- path
- }
- }
+ pub fn new(path: &'a Path) -> BasicFSResolver<'a> {
+ BasicFSResolver { path }
+ }
}
impl<'a> TextureResolver for BasicFSResolver<'a> {
- fn resolve(&mut self, tex: &Texture) -> Option<RgbaImage> {
- let path = self.path.join(&tex.name);
-
- if let Ok(file) = Reader::open(path) {
- if let Ok(guessed) = file.with_guessed_format() {
- if let Ok(decoded) = guessed.decode() {
- return Some(decoded.into_rgba());
- }
- }
- }
-
- None
- }
-} \ No newline at end of file
+ fn resolve(&mut self, tex: &Texture) -> Option<RgbaImage> {
+ let path = self.path.join(&tex.name);
+
+ if let Ok(file) = Reader::open(path) {
+ if let Ok(guessed) = file.with_guessed_format() {
+ if let Ok(decoded) = guessed.decode() {
+ return Some(decoded.into_rgba());
+ }
+ }
+ }
+
+ None
+ }
+}
diff --git a/stockton-render/src/error.rs b/stockton-render/src/error.rs
index 8922423..28d3282 100644
--- a/stockton-render/src/error.rs
+++ b/stockton-render/src/error.rs
@@ -18,35 +18,30 @@
use super::draw::target::TargetChainCreationError;
/// An error encountered creating a rendering context.
-/// Falls into 3 main types:
-/// - Hardware - No suitable card usually
-/// - 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. Could also be caused by corrupt files
#[derive(Debug)]
pub enum CreationError {
- TargetChainCreationError (TargetChainCreationError),
- WindowError,
- BadSurface,
-
- DeviceError (hal::device::CreationError),
-
- OutOfMemoryError,
-
- SyncObjectError,
-
- NoShaderC,
- ShaderCError (shaderc::Error),
- ShaderModuleFailed (hal::device::ShaderError),
- RenderPassError,
- PipelineError (hal::pso::CreationError),
- BufferError (hal::buffer::CreationError),
- BufferNoMemory,
-
- SwapchainError (hal::window::CreationError),
- ImageViewError (hal::image::ViewCreationError),
-
- BadDataError
+ TargetChainCreationError(TargetChainCreationError),
+ WindowError,
+ BadSurface,
+
+ DeviceError(hal::device::CreationError),
+
+ OutOfMemoryError,
+
+ SyncObjectError,
+
+ NoShaderC,
+ ShaderCError(shaderc::Error),
+ ShaderModuleFailed(hal::device::ShaderError),
+ RenderPassError,
+ PipelineError(hal::pso::CreationError),
+ BufferError(hal::buffer::CreationError),
+ BufferNoMemory,
+
+ SwapchainError(hal::window::CreationError),
+ ImageViewError(hal::image::ViewCreationError),
+
+ BadDataError,
}
/// An error encountered when rendering.
@@ -54,7 +49,7 @@ pub enum CreationError {
/// You'll likely need to exit or create a new context.
#[derive(Debug, Clone)]
pub enum FrameError {
- AcquireError (hal::window::AcquireError),
- SyncObjectError,
- PresentError
-} \ No newline at end of file
+ AcquireError(hal::window::AcquireError),
+ SyncObjectError,
+ PresentError,
+}
diff --git a/stockton-render/src/lib.rs b/stockton-render/src/lib.rs
index 0db4b76..7e7ea5b 100644
--- a/stockton-render/src/lib.rs
+++ b/stockton-render/src/lib.rs
@@ -21,58 +21,57 @@ extern crate gfx_hal as hal;
extern crate shaderc;
extern crate winit;
-extern crate nalgebra_glm as na;
extern crate image;
extern crate log;
+extern crate nalgebra_glm as na;
-extern crate stockton_types;
extern crate stockton_levels;
+extern crate stockton_types;
extern crate arrayvec;
+mod culling;
pub mod draw;
mod error;
mod types;
-mod culling;
-use stockton_types::World;
use stockton_levels::prelude::*;
+use stockton_types::World;
-use error::{CreationError, FrameError};
-use draw::RenderingContext;
use culling::get_visible_faces;
+use draw::RenderingContext;
+use error::{CreationError, FrameError};
/// Renders a world to a window when you tell it to.
pub struct Renderer<'a, T: MinBSPFeatures<VulkanSystem>> {
- world: World<T>,
- pub context: RenderingContext<'a>
+ world: World<T>,
+ pub context: RenderingContext<'a>,
}
-
impl<'a, T: MinBSPFeatures<VulkanSystem>> Renderer<'a, T> {
- /// Create a new Renderer.
- /// This initialises all the vulkan context, etc needed.
- pub fn new(world: World<T>, window: &winit::window::Window) -> Result<Self, CreationError> {
- let context = RenderingContext::new(window, &world.map)?;
-
- Ok(Renderer {
- world, context
- })
- }
-
- /// Render a single frame of the world
- pub fn render_frame(&mut self) -> Result<(), FrameError>{
- // Get visible faces
- let faces = get_visible_faces(self.context.camera_pos(), &self.world.map);
-
- // Then draw them
- if let Err(_) = self.context.draw_vertices(&self.world.map, &faces) {
- unsafe {self.context.handle_surface_change().unwrap()};
-
- // If it fails twice, then error
- self.context.draw_vertices(&self.world.map, &faces).map_err(|_| FrameError::PresentError)?;
- }
-
- Ok(())
- }
+ /// Create a new Renderer.
+ /// This initialises all the vulkan context, etc needed.
+ pub fn new(world: World<T>, window: &winit::window::Window) -> Result<Self, CreationError> {
+ let context = RenderingContext::new(window, &world.map)?;
+
+ Ok(Renderer { world, context })
+ }
+
+ /// Render a single frame of the world
+ pub fn render_frame(&mut self) -> Result<(), FrameError> {
+ // Get visible faces
+ let faces = get_visible_faces(self.context.camera_pos(), &self.world.map);
+
+ // Then draw them
+ if self.context.draw_vertices(&self.world.map, &faces).is_err() {
+ unsafe { self.context.handle_surface_change().unwrap() };
+
+ // If it fails twice, then error
+ self.context
+ .draw_vertices(&self.world.map, &faces)
+ .map_err(|_| FrameError::PresentError)?;
+ }
+
+ Ok(())
+ }
}
diff --git a/stockton-render/src/types.rs b/stockton-render/src/types.rs
index a0f242c..a5aefd8 100644
--- a/stockton-render/src/types.rs
+++ b/stockton-render/src/types.rs
@@ -38,4 +38,4 @@ 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
+pub type QueueGroup = hal::queue::QueueGroup<back::Backend>;