aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/render-quad/src/main.rs50
-rw-r--r--stockton-render/src/camera.rs62
-rw-r--r--stockton-render/src/level.rs77
-rw-r--r--stockton-render/src/lib.rs1
-rw-r--r--stockton-render/src/ui.rs9
-rw-r--r--stockton-render/src/window.rs7
-rw-r--r--stockton-skeleton/src/buffers/image.rs16
-rw-r--r--stockton-skeleton/src/buffers/staged.rs4
-rw-r--r--stockton-skeleton/src/buffers/staging.rs4
-rw-r--r--stockton-skeleton/src/context.rs347
-rw-r--r--stockton-skeleton/src/draw_passes/mod.rs6
-rw-r--r--stockton-skeleton/src/lib.rs70
-rw-r--r--stockton-skeleton/src/mem.rs21
-rw-r--r--stockton-skeleton/src/queue_negotiator.rs12
-rw-r--r--stockton-skeleton/src/texture/loader.rs16
-rw-r--r--stockton-skeleton/src/texture/repo.rs8
-rw-r--r--stockton-types/src/components/mod.rs14
17 files changed, 407 insertions, 317 deletions
diff --git a/examples/render-quad/src/main.rs b/examples/render-quad/src/main.rs
index 618779e..45c469a 100644
--- a/examples/render-quad/src/main.rs
+++ b/examples/render-quad/src/main.rs
@@ -19,7 +19,6 @@ use stockton_levels::{
types::Rgba,
};
use stockton_render::{
- camera::calc_vp_matrix_system,
level::{LevelDrawPass, LevelDrawPassConfig},
ui::UiDrawPass,
window::{process_window_events_system, UiState, WindowEvent, WindowFlow},
@@ -28,7 +27,7 @@ use stockton_skeleton::{
draw_passes::ConsDrawPass, error::full_error_display, texture::resolver::FsResolver, Renderer,
};
use stockton_types::{
- components::{CameraSettings, CameraVPMatrix, Transform},
+ components::{CameraSettings, Transform},
Session, Vector2, Vector3,
};
@@ -162,15 +161,10 @@ fn try_main() -> Result<()> {
let mut session = Session::new(move |schedule| {
schedule
.add_system(update_deltatime_system())
- .add_system(process_window_events_system::<
- MovementInputsManager,
- Dp<'static>,
- >())
+ .add_system(process_window_events_system::<MovementInputsManager>())
.flush()
.add_system(hello_world_system())
- .add_system(flycam_move_system::<MovementInputsManager>())
- .flush()
- .add_thread_local(calc_vp_matrix_system::<Dp<'static>>());
+ .add_system(flycam_move_system::<MovementInputsManager>());
});
session.resources.insert(map.clone());
@@ -190,7 +184,6 @@ fn try_main() -> Result<()> {
fov: 90.0,
near: 0.1,
},
- CameraVPMatrix::default(),
FlycamControlled::new(512.0, 400.0),
));
@@ -217,7 +210,7 @@ fn try_main() -> Result<()> {
ui.populate_initial_state(&renderer);
}
- session.resources.insert(renderer);
+ let mut renderer = Some(renderer);
// Done loading - This is our main loop.
// It just communicates events to the session and continuously ticks
@@ -228,15 +221,38 @@ fn try_main() -> Result<()> {
}
Event::RedrawRequested(_) => {
session.do_update();
- let mut renderer = session
- .resources
- .get_mut::<Renderer<Dp<'static>>>()
- .unwrap();
- renderer.render(&session).unwrap();
+ let r = renderer.take().unwrap();
+ match r.render(&session) {
+ Ok(r) => {
+ renderer = Some(r);
+ }
+ Err(e) => {
+ println!("Error drawing: {}", full_error_display(e));
+
+ // TODO: Not really sound
+ *(new_control_flow.write().unwrap()) = ControlFlow::Exit;
+ }
+ }
}
_ => {
if let Some(we) = WindowEvent::from(&event) {
- tx.send(we).unwrap()
+ tx.send(we).unwrap();
+
+ println!("{:?}", we);
+ if let WindowEvent::SizeChanged(_, _) = we {
+ let r = renderer.take().unwrap();
+ match r.recreate_surface(&session) {
+ Ok(r) => {
+ renderer = Some(r);
+ }
+ Err(e) => {
+ println!("Error resizing: {}", full_error_display(e));
+
+ // TODO: Not really sound
+ *(new_control_flow.write().unwrap()) = ControlFlow::Exit;
+ }
+ }
+ }
}
}
}
diff --git a/stockton-render/src/camera.rs b/stockton-render/src/camera.rs
deleted file mode 100644
index 6abc183..0000000
--- a/stockton-render/src/camera.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-//! Things related to converting 3D world space to 2D screen space
-
-use legion::maybe_changed;
-use na::{look_at_lh, perspective_lh_zo};
-use stockton_types::{
- components::{CameraSettings, CameraVPMatrix, Transform},
- Vector3,
-};
-
-use stockton_skeleton::{
- draw_passes::{DrawPass, Singular},
- Renderer,
-};
-
-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(),
- )
-}
-
-#[system(for_each)]
-#[filter(maybe_changed::<Transform>() | maybe_changed::<CameraSettings>())]
-pub fn calc_vp_matrix<DP: DrawPass<Singular> + 'static>(
- transform: &Transform,
- settings: &CameraSettings,
- matrix: &mut CameraVPMatrix,
- #[resource] renderer: &Renderer<DP>,
-) {
- // Get look direction from euler angles
- let direction = euler_to_direction(&transform.rotation);
-
- // Converts world space to camera space
- let view_matrix = look_at_lh(
- &transform.position,
- &(transform.position + direction),
- &Vector3::new(0.0, 1.0, 0.0),
- );
-
- // Converts camera space to screen space
- let projection_matrix = {
- let mut temp = perspective_lh_zo(
- renderer.get_aspect_ratio(),
- settings.fov,
- settings.near,
- settings.far,
- );
-
- // Vulkan's co-ord system is different from OpenGLs
- temp[(1, 1)] *= -1.0;
-
- temp
- };
-
- // Chain them together into a single matrix
- matrix.vp_matrix = projection_matrix * view_matrix;
-}
diff --git a/stockton-render/src/level.rs b/stockton-render/src/level.rs
index 3851cee..29ab7c5 100644
--- a/stockton-render/src/level.rs
+++ b/stockton-render/src/level.rs
@@ -22,18 +22,10 @@ use stockton_skeleton::{
types::*,
};
use stockton_types::{
- components::{CameraSettings, CameraVPMatrix, Transform},
+ components::{CameraSettings, Transform},
*,
};
-use std::{
- array::IntoIter,
- convert::TryInto,
- iter::{empty, once},
- marker::PhantomData,
- sync::{Arc, RwLock},
-};
-
use anyhow::{Context, Result};
use hal::{
buffer::SubRange,
@@ -48,7 +40,15 @@ use hal::{
},
};
use legion::{Entity, IntoQuery};
+use na::{look_at_lh, perspective_lh_zo};
use shaderc::ShaderKind;
+use std::{
+ array::IntoIter,
+ convert::TryInto,
+ iter::{empty, once},
+ marker::PhantomData,
+ sync::{Arc, RwLock},
+};
use thiserror::Error;
/// The Vertexes that go to the shader
@@ -90,10 +90,43 @@ where
.record_commit_cmds(cmd_buffer)?;
// Get level & camera
- let mut query = <(&Transform, &CameraSettings, &CameraVPMatrix)>::query();
- let (camera_transform, camera_settings, camera_vp) = query
+ let mut query = <(&Transform, &CameraSettings)>::query();
+ let (camera_transform, camera_settings) = query
.get(&session.world, self.active_camera)
.context("Couldn't find camera components")?;
+
+ let camera_vp = {
+ let aspect_ratio =
+ self.pipeline.render_area.w as f32 / self.pipeline.render_area.h as f32;
+
+ // Get look direction from euler angles
+ let direction = euler_to_direction(&camera_transform.rotation);
+
+ // Converts world space to camera space
+ let view_matrix = look_at_lh(
+ &camera_transform.position,
+ &(camera_transform.position + direction),
+ &Vector3::new(0.0, 1.0, 0.0),
+ );
+
+ // Converts camera space to screen space
+ let projection_matrix = {
+ let mut temp = perspective_lh_zo(
+ aspect_ratio,
+ camera_settings.fov,
+ camera_settings.near,
+ camera_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
+ };
let map_lock: Arc<RwLock<M>> = session.resources.get::<Arc<RwLock<M>>>().unwrap().clone();
let map = map_lock.read().map_err(|_| LockPoisoned::Map)?;
@@ -131,7 +164,7 @@ where
cmd_buffer.bind_graphics_pipeline(&self.pipeline.pipeline);
// VP Matrix
- let vp = &*(camera_vp.vp_matrix.data.as_slice() as *const [f32] as *const [u32]);
+ let vp = &*(camera_vp.data.as_slice() as *const [f32] as *const [u32]);
cmd_buffer.push_graphics_constants(
&self.pipeline.pipeline_layout,
@@ -238,7 +271,7 @@ where
fn deactivate(self, context: &mut RenderingContext) -> Result<()> {
self.draw_buffers.deactivate(context);
unsafe {
- let mut device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let mut device = context.lock_device()?;
self.pipeline.deactivate(&mut device);
for fb in self.framebuffers.dissolve() {
device.destroy_framebuffer(fb);
@@ -402,7 +435,7 @@ where
let draw_buffers =
DrawBuffers::from_context(context).context("Error creating draw buffers")?;
let (pipeline, framebuffers) = {
- let mut device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let mut device = context.lock_device()?;
let pipeline = spec
.build(
&mut device,
@@ -457,8 +490,8 @@ where
})
}
- fn find_aux_queues<'c>(
- adapter: &'c Adapter,
+ fn find_aux_queues(
+ adapter: &Adapter,
queue_negotiator: &mut QueueFamilyNegotiator,
) -> Result<()> {
queue_negotiator.find(adapter, &TexLoadQueue, 1)?;
@@ -473,3 +506,15 @@ pub enum LevelError {
#[error("Referential Integrity broken")]
BadReference,
}
+
+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(),
+ )
+}
diff --git a/stockton-render/src/lib.rs b/stockton-render/src/lib.rs
index e3e5bf8..34f5117 100644
--- a/stockton-render/src/lib.rs
+++ b/stockton-render/src/lib.rs
@@ -3,7 +3,6 @@ extern crate legion;
extern crate gfx_hal as hal;
extern crate nalgebra_glm as na;
-pub mod camera;
pub mod level;
pub mod ui;
pub mod window;
diff --git a/stockton-render/src/ui.rs b/stockton-render/src/ui.rs
index f688f42..474c1dd 100644
--- a/stockton-render/src/ui.rs
+++ b/stockton-render/src/ui.rs
@@ -9,7 +9,6 @@ use stockton_skeleton::{
},
context::RenderingContext,
draw_passes::{util::TargetSpecificResources, DrawPass, IntoDrawPass, PassPosition},
- error::LockPoisoned,
mem::{DataPool, StagingPool, TexturesPool},
queue_negotiator::QueueFamilyNegotiator,
texture::{
@@ -201,7 +200,7 @@ impl<'a, P: PassPosition> DrawPass<P> for UiDrawPass<'a> {
self.draw_buffers.deactivate(context);
unsafe {
- let mut device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let mut device = context.lock_device()?;
self.pipeline.deactivate(&mut device);
for fb in self.framebuffers.dissolve() {
device.destroy_framebuffer(fb);
@@ -307,7 +306,7 @@ impl<'a, P: PassPosition> IntoDrawPass<UiDrawPass<'a>, P> for () {
DrawBuffers::from_context(context).context("Error creating draw buffers")?;
let (pipeline, framebuffers) = {
- let mut device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let mut device = context.lock_device()?;
let pipeline = spec
.build(
@@ -339,8 +338,8 @@ impl<'a, P: PassPosition> IntoDrawPass<UiDrawPass<'a>, P> for () {
})
}
- fn find_aux_queues<'c>(
- adapter: &'c Adapter,
+ fn find_aux_queues(
+ adapter: &Adapter,
queue_negotiator: &mut QueueFamilyNegotiator,
) -> Result<()> {
queue_negotiator.find(adapter, &TexLoadQueue, 1)?;
diff --git a/stockton-render/src/window.rs b/stockton-render/src/window.rs
index e6cf7fb..429a3c2 100644
--- a/stockton-render/src/window.rs
+++ b/stockton-render/src/window.rs
@@ -216,7 +216,7 @@ impl WindowFlow {
#[system]
/// A system to process the window events sent to renderer by the winit event loop.
-pub fn _process_window_events<T: 'static + InputManager, DP: 'static + DrawPass<Singular>>(
+pub fn _process_window_events<T: 'static + InputManager>(
#[resource] window_channel: &mut WindowFlow,
#[resource] manager: &mut T,
#[resource] mouse: &mut Mouse,
@@ -273,7 +273,6 @@ pub fn _process_window_events<T: 'static + InputManager, DP: 'static + DrawPass<
manager.handle_frame(&actions_buf[0..actions_buf_cursor]);
}
-pub fn process_window_events_system<T: 'static + InputManager, DP: 'static + DrawPass<Singular>>(
-) -> impl Runnable {
- _process_window_events_system::<T, DP>(Vec::with_capacity(4))
+pub fn process_window_events_system<T: 'static + InputManager>() -> impl Runnable {
+ _process_window_events_system::<T>(Vec::with_capacity(4))
}
diff --git a/stockton-skeleton/src/buffers/image.rs b/stockton-skeleton/src/buffers/image.rs
index 4278585..820561f 100644
--- a/stockton-skeleton/src/buffers/image.rs
+++ b/stockton-skeleton/src/buffers/image.rs
@@ -73,7 +73,7 @@ impl<P: MemoryPool> BoundImageView<P> {
.write()
.map_err(|_| LockPoisoned::MemoryPool)?;
- let mut device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let mut device = context.lock_device()?;
let row_alignment_mask = context
.physical_device_properties()
.limits
@@ -149,11 +149,7 @@ impl<P: MemoryPool> BoundImageView<P> {
/// Destroy all vulkan objects. Must be called before dropping.
pub fn deactivate_with_context(self, context: &mut RenderingContext) {
- let mut device = context
- .device()
- .write()
- .map_err(|_| LockPoisoned::Device)
- .unwrap();
+ let mut device = context.lock_device().unwrap();
let mut pool = context
.existing_memory_pool::<P>()
.unwrap()
@@ -227,7 +223,7 @@ impl<P: MemoryPool> SampledImage<P> {
.write()
.map_err(|_| LockPoisoned::MemoryPool)?;
- let mut device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let mut device = context.lock_device()?;
let row_alignment_mask = context
.physical_device_properties()
.limits
@@ -267,11 +263,7 @@ impl<P: MemoryPool> SampledImage<P> {
/// Destroy all vulkan objects. Must be called before dropping.
pub fn deactivate_with_context(self, context: &mut RenderingContext) {
- let mut device = context
- .device()
- .write()
- .map_err(|_| LockPoisoned::Device)
- .unwrap();
+ let mut device = context.lock_device().unwrap();
let mut pool = context
.existing_memory_pool::<P>()
.unwrap()
diff --git a/stockton-skeleton/src/buffers/staged.rs b/stockton-skeleton/src/buffers/staged.rs
index ec42102..2ece045 100644
--- a/stockton-skeleton/src/buffers/staged.rs
+++ b/stockton-skeleton/src/buffers/staged.rs
@@ -56,7 +56,7 @@ where
context.ensure_memory_pool::<SP>()?;
// Lock the device and memory pools
- let mut device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let mut device = context.lock_device()?;
let mut mempool = context
.existing_memory_pool::<P>()
.unwrap()
@@ -111,7 +111,7 @@ where
/// Destroy all Vulkan objects. Should be called before dropping.
pub fn deactivate(mut self, context: &mut RenderingContext) {
unsafe {
- let device = &mut *context.device().write().unwrap();
+ let device = &mut *context.lock_device().unwrap();
self.staged_memory.unmap(device).unwrap();
diff --git a/stockton-skeleton/src/buffers/staging.rs b/stockton-skeleton/src/buffers/staging.rs
index 44d0c2d..35cac00 100644
--- a/stockton-skeleton/src/buffers/staging.rs
+++ b/stockton-skeleton/src/buffers/staging.rs
@@ -27,7 +27,7 @@ where
pub fn from_context(context: &mut RenderingContext, size: u64) -> Result<Self> {
context.ensure_memory_pool::<P>()?;
- let mut device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let mut device = context.lock_device()?;
let mut mempool = context
.existing_memory_pool()
.unwrap()
@@ -70,7 +70,7 @@ where
}
pub fn deactivate_context(self, context: &mut RenderingContext) {
- let mut device = context.device().write().unwrap();
+ let mut device = context.lock_device().unwrap();
let mut mempool = context.existing_memory_pool().unwrap().write().unwrap();
self.deactivate_device_pool(&mut device, &mut mempool)
diff --git a/stockton-skeleton/src/context.rs b/stockton-skeleton/src/context.rs
index bef9f9d..ea9810f 100644
--- a/stockton-skeleton/src/context.rs
+++ b/stockton-skeleton/src/context.rs
@@ -4,17 +4,19 @@
use std::{
any::{Any, TypeId},
collections::HashMap,
+ marker::PhantomData,
mem::ManuallyDrop,
ptr::read,
- sync::{Arc, RwLock},
+ sync::{Arc, RwLock, RwLockWriteGuard},
};
-use anyhow::{Context, Result};
+use anyhow::{anyhow, Context, Result};
use hal::{
format::{ChannelType, Format, ImageFeature},
image::{Extent, FramebufferAttachment, Usage, ViewCapabilities},
pool::CommandPoolCreateFlags,
pso::Viewport,
+ queue::QueueFamilyId,
window::{CompositeAlphaMode, PresentMode},
PhysicalDeviceProperties,
};
@@ -29,17 +31,16 @@ use super::{
};
use crate::{
draw_passes::Singular,
- error::{EnvironmentError, LockPoisoned},
+ error::{EnvironmentError, LockPoisoned, UsageError},
mem::MemoryPool,
- queue_negotiator::QueueFamilyNegotiator,
+ queue_negotiator::{QueueFamilyNegotiator, QueueFamilySelector, SharedQueue},
types::*,
};
use stockton_types::Session;
-/// Contains most root vulkan objects, and some precalculated info such as best formats to use.
-/// In most cases, this and the DrawPass should contain all vulkan objects present.
-pub struct RenderingContext {
+/// The actual data behind [`StatefulRenderingContext`]
+struct InnerRenderingContext {
/// Vulkan Instance
instance: ManuallyDrop<back::Instance>,
@@ -75,7 +76,43 @@ pub struct RenderingContext {
properties: ContextProperties,
}
-impl RenderingContext {
+/// A type enum for different states the `RenderingContext` can be in.
+pub trait RenderingContextState: private::Sealed {}
+
+/// Normal operation.
+pub struct Normal;
+impl RenderingContextState for Normal {}
+
+/// The last draw failed, most likely meaning the surface needs re-created, or the entire context is toast.
+pub struct LastDrawFailed;
+impl RenderingContextState for LastDrawFailed {}
+
+/// All memory pools have been deactivated. This should only be used when shutting down
+pub struct DeactivatedMemoryPools;
+impl RenderingContextState for DeactivatedMemoryPools {}
+
+/// Seal `RenderingContextState`
+mod private {
+ pub trait Sealed {}
+ impl Sealed for super::Normal {}
+ impl Sealed for super::LastDrawFailed {}
+ impl Sealed for super::DeactivatedMemoryPools {}
+}
+
+/// Contains most root vulkan objects, and some precalculated info such as best formats to use.
+/// In most cases, this and the DrawPass should contain all Vulkan objects present.
+/// [`RenderingContext`] is a convenience type that applies in most situations.
+pub struct StatefulRenderingContext<S: RenderingContextState>(
+ /// The actual data. This is boxed so that there's less overhead when transitioning type state.
+ Box<InnerRenderingContext>,
+ PhantomData<S>,
+);
+
+/// Convenience type, since we often want to refer to normal operation
+pub type RenderingContext = StatefulRenderingContext<Normal>;
+
+/// Methods only implemented in normal operation
+impl StatefulRenderingContext<Normal> {
/// Create a new RenderingContext for the given window.
pub fn new<IDP: IntoDrawPass<DP, Singular>, DP: DrawPass<Singular>>(
window: &Window,
@@ -96,7 +133,7 @@ impl RenderingContext {
let adapter = adapters.remove(0);
// Queue Negotiator
- let (queue_negotiator, surface) = {
+ let (family_negotiator, surface) = {
let dq: DrawQueue = DrawQueue { surface };
let mut qn = QueueFamilyNegotiator::new();
@@ -117,7 +154,7 @@ impl RenderingContext {
// TODO: This sucks, but hal is restrictive on how we can pass this specific argument.
// Deduplicate families & convert to specific type.
- let open_spec = queue_negotiator.get_open_spec(&adapter);
+ let open_spec = family_negotiator.get_open_spec(&adapter);
let gpu = unsafe {
adapter
@@ -129,143 +166,108 @@ impl RenderingContext {
(Arc::new(RwLock::new(gpu.device)), gpu.queue_groups)
};
- let mut queue_negotiator = queue_negotiator.finish(queue_groups);
+ let mut queue_negotiator = family_negotiator.finish(queue_groups);
// Context properties
let properties = ContextProperties::find_best(&adapter, &surface)
.context("Error getting context properties")?;
- // Lock device
- let mut device = device_lock
- .write()
- .map_err(|_| LockPoisoned::Device)
- .context("Error getting device lock")?;
-
- debug!("Detected swapchain properties: {:?}", properties);
-
- // Command pool
- let mut cmd_pool = unsafe {
- device.create_command_pool(
- queue_negotiator
- .family::<DrawQueue>()
- .ok_or(EnvironmentError::NoSuitableFamilies)?,
- CommandPoolCreateFlags::RESET_INDIVIDUAL,
- )
- }
- .context("Error creating draw command pool")?;
-
- // Swapchain and associated resources
- let target_chain = TargetChain::new(&mut device, surface, &mut cmd_pool, &properties)
- .context("Error creating target chain")?;
-
- // Unlock device
- drop(device);
+ debug!("Detected context properties: {:?}", properties);
+
+ let (cmd_pool, target_chain) = {
+ // Lock device
+ let mut device = device_lock
+ .write()
+ .map_err(|_| LockPoisoned::Device)
+ .context("Error getting device lock")?;
+
+ // Command pool
+ let mut cmd_pool = unsafe {
+ device.create_command_pool(
+ queue_negotiator
+ .family::<DrawQueue>()
+ .ok_or(EnvironmentError::NoSuitableFamilies)?,
+ CommandPoolCreateFlags::RESET_INDIVIDUAL,
+ )
+ }
+ .context("Error creating draw command pool")?;
+
+ // Swapchain and associated resources
+ let target_chain = TargetChain::new(&mut device, surface, &mut cmd_pool, &properties)
+ .context("Error creating target chain")?;
+
+ (cmd_pool, target_chain)
+ };
let queue = queue_negotiator
.get_queue::<DrawQueue>()
.context("Error getting draw queue")?;
- Ok(RenderingContext {
- instance: ManuallyDrop::new(instance),
+ Ok(StatefulRenderingContext(
+ Box::new(InnerRenderingContext {
+ instance: ManuallyDrop::new(instance),
- device: device_lock,
- physical_device_properties: adapter.physical_device.properties(),
- adapter,
+ device: device_lock,
+ physical_device_properties: adapter.physical_device.properties(),
+ adapter,
- queue_negotiator,
- queue,
+ queue_negotiator,
+ queue,
- target_chain: ManuallyDrop::new(target_chain),
- cmd_pool: ManuallyDrop::new(cmd_pool),
+ target_chain: ManuallyDrop::new(target_chain),
+ cmd_pool: ManuallyDrop::new(cmd_pool),
- pixels_per_point: window.scale_factor() as f32,
- memory_pools: HashMap::new(),
- properties,
- })
+ pixels_per_point: window.scale_factor() as f32,
+ memory_pools: HashMap::new(),
+ properties,
+ }),
+ PhantomData,
+ ))
}
- /// 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<()> {
- let mut device = self
- .device
- .write()
- .map_err(|_| LockPoisoned::Device)
- .context("Error getting device lock")?;
-
- device
- .wait_idle()
- .context("Error waiting for device to become idle")?;
-
- let surface = ManuallyDrop::into_inner(read(&self.target_chain))
- .deactivate_with_recyling(&mut device, &mut self.cmd_pool);
-
- self.properties = ContextProperties::find_best(&self.adapter, &surface)
- .context("Error finding best swapchain properties")?;
-
- self.target_chain = ManuallyDrop::new(
- TargetChain::new(&mut device, surface, &mut self.cmd_pool, &self.properties)
- .context("Error creating target chain")?,
- );
- Ok(())
+ /// Draw onto the next frame of the swapchain.
+ /// This takes ownership so we can transition to `LastDrawFailed` if an error occurs.
+ /// If it does, you can try to recover with [`StatefulRenderingContext::attempt_recovery`]
+ pub fn draw_next_frame<DP: DrawPass<Singular>>(
+ mut self,
+ session: &Session,
+ dp: &mut DP,
+ ) -> Result<RenderingContext, (anyhow::Error, StatefulRenderingContext<LastDrawFailed>)> {
+ if let Err(e) = self.attempt_draw_next_frame(session, dp) {
+ Err((e, StatefulRenderingContext(self.0, PhantomData)))
+ } else {
+ Ok(self)
+ }
}
- /// Draw onto the next frame of the swapchain
- pub fn draw_next_frame<DP: DrawPass<Singular>>(
+ /// The actual drawing attempt
+ fn attempt_draw_next_frame<DP: DrawPass<Singular>>(
&mut self,
session: &Session,
dp: &mut DP,
) -> Result<()> {
+ // Lock device & queue. We can't use our nice convenience function, because of borrowing issues
let mut device = self
+ .0
.device
.write()
.map_err(|_| LockPoisoned::Device)
.context("Error getting device lock")?;
let mut queue = self
+ .0
.queue
.write()
.map_err(|_| LockPoisoned::Queue)
.context("Error getting draw queue lock")?;
- // Level draw pass
- self.target_chain
+ self.0
+ .target_chain
.do_draw_with(&mut device, &mut queue, dp, session)
.context("Error preparing next target")?;
Ok(())
}
- /// Get a reference to the rendering context's pixels per point.
- pub fn pixels_per_point(&self) -> f32 {
- self.pixels_per_point
- }
-
- /// Get a reference to the rendering context's device.
- pub fn device(&self) -> &Arc<RwLock<DeviceT>> {
- &self.device
- }
-
- /// Get a reference to the rendering context's target chain.
- pub fn target_chain(&self) -> &TargetChain {
- &self.target_chain
- }
-
- /// Get a reference to the rendering context's adapter.
- pub fn adapter(&self) -> &Adapter {
- &self.adapter
- }
-
- /// Get a mutable reference to the rendering context's queue negotiator.
- pub fn queue_negotiator_mut(&mut self) -> &mut QueueNegotiator {
- &mut self.queue_negotiator
- }
-
- /// Get a reference to the physical device's properties.
- pub fn physical_device_properties(&self) -> &PhysicalDeviceProperties {
- &self.physical_device_properties
- }
-
/// Get the specified memory pool, lazily initialising it if it's not yet present
pub fn memory_pool<P: MemoryPool>(&mut self) -> Result<&Arc<RwLock<P>>> {
self.ensure_memory_pool::<P>()?;
@@ -276,8 +278,9 @@ impl RenderingContext {
#[allow(clippy::map_entry)] // We can't follow the suggestion because of a borrowing issue
pub fn ensure_memory_pool<P: MemoryPool>(&mut self) -> Result<()> {
let tid = TypeId::of::<P>();
- if !self.memory_pools.contains_key(&tid) {
- self.memory_pools
+ if !self.0.memory_pools.contains_key(&tid) {
+ self.0
+ .memory_pools
.insert(tid, Box::new(P::from_context(self)?));
}
Ok(())
@@ -287,36 +290,130 @@ impl RenderingContext {
/// You should only use this when you're certain it exists, such as when freeing memory
/// allocated from that pool
pub fn existing_memory_pool<P: MemoryPool>(&self) -> Option<&Arc<RwLock<P>>> {
- self.memory_pools
+ self.0
+ .memory_pools
.get(&TypeId::of::<P>())
.map(|x| x.downcast_ref().unwrap())
}
+ /// Deactivate all stored memory pools.
+ pub fn deactivate_memory_pools(
+ self,
+ ) -> Result<StatefulRenderingContext<DeactivatedMemoryPools>> {
+ // TODO: Properly deactivate memory pools
+
+ Ok(StatefulRenderingContext(self.0, PhantomData))
+ }
+}
+
+impl StatefulRenderingContext<LastDrawFailed> {
+ /// If this function fails the whole context is probably dead
+ pub fn attempt_recovery(self) -> Result<RenderingContext> {
+ let this = self.recreate_surface()?;
+ Ok(StatefulRenderingContext(this.0, PhantomData))
+ }
+}
+
+// Methods implemented for all states
+impl<S: RenderingContextState> StatefulRenderingContext<S> {
+ /// Get the current pixels per point.
+ pub fn pixels_per_point(&self) -> f32 {
+ self.0.pixels_per_point
+ }
+
+ /// Get a new reference to the lock for the device used by this context.
+ /// This can be used when instantiating code that runs in another thread.
+ pub fn clone_device_lock(&self) -> Arc<RwLock<DeviceT>> {
+ self.0.device.clone()
+ }
+
+ /// Lock the device used by this rendering context
+ pub fn lock_device(&self) -> Result<RwLockWriteGuard<'_, DeviceT>> {
+ Ok(self.0.device.write().map_err(|_| LockPoisoned::Device)?)
+ }
+
+ /// Get a reference to the rendering context's adapter.
+ pub fn adapter(&self) -> &Adapter {
+ &self.0.adapter
+ }
+
+ /// Get a shared queue from the family that was selected with T.
+ /// You should already have called [`crate::queue_negotiator::QueueFamilyNegotiator::find`], otherwise this will return an error.
+ pub fn get_queue<T: QueueFamilySelector>(&mut self) -> Result<SharedQueue> {
+ self.0.queue_negotiator.get_queue::<T>()
+ }
+
+ /// Get the family that was selected by T.
+ /// You should already have called [`crate::queue_negotiator::QueueFamilyNegotiator::find`], otherwise this will return an error.
+ pub fn get_queue_family<T: QueueFamilySelector>(&self) -> Result<QueueFamilyId> {
+ self.0
+ .queue_negotiator
+ .family::<T>()
+ .ok_or(anyhow!(UsageError::QueueNegotiatorMisuse))
+ }
+
+ /// Get a reference to the physical device's properties.
+ pub fn physical_device_properties(&self) -> &PhysicalDeviceProperties {
+ &self.0.physical_device_properties
+ }
/// Get a reference to the rendering context's properties.
pub fn properties(&self) -> &ContextProperties {
- &self.properties
+ &self.0.properties
}
-}
-impl core::ops::Drop for RenderingContext {
- fn drop(&mut self) {
- {
- self.device.write().unwrap().wait_idle().unwrap();
+ /// Recreate the surface, swapchain, and other derived components.
+ pub fn recreate_surface(mut self) -> Result<Self> {
+ // TODO: Deactivate if this fails
+ unsafe {
+ let mut device = self
+ .0
+ .device
+ .write()
+ .map_err(|_| LockPoisoned::Device)
+ .context("Error getting device lock")?;
+
+ device
+ .wait_idle()
+ .context("Error waiting for device to become idle")?;
+
+ let surface = ManuallyDrop::into_inner(read(&self.0.target_chain))
+ .deactivate_with_recyling(&mut device, &mut self.0.cmd_pool);
+
+ self.0.properties = ContextProperties::find_best(&self.0.adapter, &surface)
+ .context("Error finding best swapchain properties")?;
+
+ // TODO: This is unsound, if we return an error here `self.0.TargetChain` may be accessed again.
+ self.0.target_chain = ManuallyDrop::new(
+ TargetChain::new(
+ &mut device,
+ surface,
+ &mut self.0.cmd_pool,
+ &self.0.properties,
+ )
+ .context("Error creating target chain")?,
+ );
}
- // TODO: Better deactivation code
+ Ok(StatefulRenderingContext(self.0, PhantomData))
+ }
+}
+
+// Methods only implemented after we start deactivating
+impl StatefulRenderingContext<DeactivatedMemoryPools> {
+ pub fn deactivate(mut self) -> Result<()> {
+ self.lock_device()?.wait_idle()?;
+ // TODO: The rest of the deactivation code needs updated.
unsafe {
- let mut device = self.device.write().unwrap();
+ let mut device = self.0.device.write().map_err(|_| LockPoisoned::Device)?;
- ManuallyDrop::into_inner(read(&self.target_chain)).deactivate(
- &mut self.instance,
- &mut device,
- &mut self.cmd_pool,
- );
+ let target_chain = ManuallyDrop::take(&mut self.0.target_chain);
+ target_chain.deactivate(&mut self.0.instance, &mut device, &mut self.0.cmd_pool);
- device.destroy_command_pool(ManuallyDrop::into_inner(read(&self.cmd_pool)));
+ device.destroy_command_pool(ManuallyDrop::into_inner(self.0.cmd_pool));
}
+
+ Ok(())
}
}
diff --git a/stockton-skeleton/src/draw_passes/mod.rs b/stockton-skeleton/src/draw_passes/mod.rs
index d830fe5..cdc983f 100644
--- a/stockton-skeleton/src/draw_passes/mod.rs
+++ b/stockton-skeleton/src/draw_passes/mod.rs
@@ -1,7 +1,7 @@
//! Traits and common draw passes.
use std::ops::Range;
-use crate::{queue_negotiator::QueueFamilyNegotiator, types::*, RenderingContext};
+use crate::{context::RenderingContext, queue_negotiator::QueueFamilyNegotiator, types::*};
use hal::{
image::Layout,
pass::{AttachmentLoadOp, AttachmentOps, AttachmentStoreOp},
@@ -44,8 +44,8 @@ pub trait IntoDrawPass<T: DrawPass<P>, P: PassPosition> {
/// This function should ask the queue negotatior to find families for any auxilary operations this draw pass needs to perform
/// For example, .find(&TexLoadQueue)
- fn find_aux_queues<'a>(
- adapter: &'a Adapter,
+ fn find_aux_queues(
+ adapter: &Adapter,
queue_negotiator: &mut QueueFamilyNegotiator,
) -> Result<()>;
}
diff --git a/stockton-skeleton/src/lib.rs b/stockton-skeleton/src/lib.rs
index b514531..6260753 100644
--- a/stockton-skeleton/src/lib.rs
+++ b/stockton-skeleton/src/lib.rs
@@ -18,6 +18,8 @@ pub mod texture;
pub mod types;
pub mod utils;
+use std::mem::ManuallyDrop;
+
use context::RenderingContext;
use draw_passes::{DrawPass, IntoDrawPass, Singular};
@@ -30,7 +32,7 @@ use winit::window::Window;
/// Also takes ownership of the window and channels window events to be processed outside winit's event loop.
pub struct Renderer<DP> {
/// All the vulkan stuff
- context: RenderingContext,
+ context: ManuallyDrop<RenderingContext>,
/// The draw pass we're using
draw_pass: DP,
@@ -50,25 +52,53 @@ impl<DP: DrawPass<Singular>> Renderer<DP> {
.init(session, &mut context)
.context("Error initialising draw pass")?;
- Ok(Renderer { context, draw_pass })
+ Ok(Renderer {
+ context: ManuallyDrop::new(context),
+ draw_pass,
+ })
}
/// Render a single frame of the given session.
- pub fn render(&mut self, session: &Session) -> Result<()> {
- // Try to draw
- if self
- .context
- .draw_next_frame(session, &mut self.draw_pass)
- .is_err()
- {
- // Probably the surface changed
- self.handle_surface_change(session)?;
-
- // If it fails twice, then error
- self.context.draw_next_frame(session, &mut self.draw_pass)?;
+ /// If this returns an error, the whole renderer is dead, hence it takes ownership to ensure it can't be called in that case.
+ pub fn render(mut self, session: &Session) -> Result<Renderer<DP>> {
+ // Safety: If this fails at any point, the ManuallyDrop won't be touched again, as Renderer will be dropped.
+ // Hence, we can always take from the ManuallyDrop
+ unsafe {
+ match ManuallyDrop::take(&mut self.context)
+ .draw_next_frame(session, &mut self.draw_pass)
+ {
+ Ok(c) => {
+ self.context = ManuallyDrop::new(c);
+ Ok(self)
+ }
+ Err((_e, c)) => {
+ // TODO: Try to detect if the error is actually surface related.
+ let c = c.attempt_recovery()?;
+ match c.draw_next_frame(session, &mut self.draw_pass) {
+ Ok(c) => {
+ self.context = ManuallyDrop::new(c);
+ Ok(self)
+ }
+ Err((e, _c)) => Err(e),
+ }
+ }
+ }
+ }
+ }
+
+ /// Recreate the surface, and other derived components.
+ /// This should be called when the window is resized.
+ pub fn recreate_surface(mut self, session: &Session) -> Result<Renderer<DP>> {
+ // Safety: If this fails at any point, the ManuallyDrop won't be touched again, as Renderer will be dropped.
+ // Hence, we can always take from the ManuallyDrop
+ unsafe {
+ let ctx = ManuallyDrop::take(&mut self.context).recreate_surface()?;
+ self.context = ManuallyDrop::new(ctx);
}
+ self.draw_pass
+ .handle_surface_change(session, &mut self.context)?;
- Ok(())
+ Ok(self)
}
pub fn get_aspect_ratio(&self) -> f32 {
@@ -76,16 +106,6 @@ impl<DP: DrawPass<Singular>> Renderer<DP> {
e.width as f32 / e.height as f32
}
- pub fn handle_surface_change(&mut self, session: &Session) -> Result<()> {
- unsafe {
- self.context.handle_surface_change()?;
- self.draw_pass
- .handle_surface_change(session, &mut self.context)?;
- }
-
- Ok(())
- }
-
/// Get a reference to the renderer's context.
pub fn context(&self) -> &RenderingContext {
&self.context
diff --git a/stockton-skeleton/src/mem.rs b/stockton-skeleton/src/mem.rs
index 03d74ea..7c31712 100644
--- a/stockton-skeleton/src/mem.rs
+++ b/stockton-skeleton/src/mem.rs
@@ -4,7 +4,10 @@
//! using [`RenderingContext.pool_allocator`]
//! Alternatively, some default memory pools are availble when the feature `rendy_pools` is used (on by default).
-use crate::{context::RenderingContext, types::*};
+use crate::{
+ context::{DeactivatedMemoryPools, RenderingContext, StatefulRenderingContext},
+ types::*,
+};
use std::{
ops::Range,
@@ -36,7 +39,7 @@ pub trait MemoryPool: Send + Sync + 'static {
fn free(&mut self, device: &DeviceT, block: Self::Block) -> u64;
/// Deactivate this memory pool, freeing any allocated memory objects.
- fn deactivate(self, context: &mut RenderingContext);
+ fn deactivate(self, context: &mut StatefulRenderingContext<DeactivatedMemoryPools>);
}
/// Block that owns a `Range` of the `Memory`.
@@ -78,7 +81,7 @@ mod rendy {
use super::*;
use crate::{
- error::{EnvironmentError, LockPoisoned, UsageError},
+ error::{EnvironmentError, UsageError},
utils::find_memory_type_id,
};
@@ -128,7 +131,7 @@ mod rendy {
// Size and alignment don't necessarily stay the same, so we're forced to
// guess at the alignment for our allocator.
- let device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let device = context.lock_device()?;
let img = device
.create_image(
Kind::D2(16, 16, 1, 1),
@@ -170,7 +173,7 @@ mod rendy {
Ok(Arc::new(RwLock::new(Self(allocator))))
}
- fn deactivate(self, _context: &mut RenderingContext) {
+ fn deactivate(self, _context: &mut StatefulRenderingContext<DeactivatedMemoryPools>) {
self.0.dispose();
}
}
@@ -193,7 +196,7 @@ mod rendy {
let type_mask = unsafe {
use hal::image::{Kind, Tiling, Usage, ViewCapabilities};
- let device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let device = context.lock_device()?;
let img = device
.create_image(
Kind::D2(16, 16, 1, 1),
@@ -235,7 +238,7 @@ mod rendy {
Ok(Arc::new(RwLock::new(Self(allocator))))
}
- fn deactivate(self, _context: &mut RenderingContext) {
+ fn deactivate(self, _context: &mut StatefulRenderingContext<DeactivatedMemoryPools>) {
self.0.dispose()
}
}
@@ -277,7 +280,7 @@ mod rendy {
Ok(Arc::new(RwLock::new(StagingPool(allocator))))
}
- fn deactivate(self, _context: &mut RenderingContext) {
+ fn deactivate(self, _context: &mut StatefulRenderingContext<DeactivatedMemoryPools>) {
self.0.dispose()
}
}
@@ -318,7 +321,7 @@ mod rendy {
Ok(Arc::new(RwLock::new(DataPool(allocator))))
}
- fn deactivate(self, _context: &mut RenderingContext) {
+ fn deactivate(self, _context: &mut StatefulRenderingContext<DeactivatedMemoryPools>) {
self.0.dispose()
}
}
diff --git a/stockton-skeleton/src/queue_negotiator.rs b/stockton-skeleton/src/queue_negotiator.rs
index b78fe33..765516b 100644
--- a/stockton-skeleton/src/queue_negotiator.rs
+++ b/stockton-skeleton/src/queue_negotiator.rs
@@ -70,7 +70,7 @@ impl QueueFamilyNegotiator {
mut count: usize,
) -> Result<()> {
if let Entry::Occupied(e) = self.family_ids.entry(TypeId::of::<T>()) {
- count = count + e.get().0;
+ count += e.get().0;
}
let candidates: Vec<&QueueFamilyT> = adapter
@@ -123,7 +123,7 @@ impl QueueFamilyNegotiator {
}
/// Finish selecting our queue families, and turn this into a `QueueNegotiator`
- pub fn finish<'a>(self, queue_groups: Vec<QueueGroup>) -> QueueNegotiator {
+ pub fn finish(self, queue_groups: Vec<QueueGroup>) -> QueueNegotiator {
QueueNegotiator {
family_ids: self.family_ids,
already_allocated: HashMap::new(),
@@ -132,6 +132,12 @@ impl QueueFamilyNegotiator {
}
}
+impl Default for QueueFamilyNegotiator {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
/// Used internally in calls to [`hal::adapter::PhysicalDevice::open`]
pub(crate) struct AdapterOpenSpec<'a>(Box<[(&'a QueueFamilyT, Vec<f32>)]>);
@@ -164,7 +170,7 @@ impl QueueNegotiator {
/// You should already have called [`self::QueueFamilyNegotiator::find`], otherwise this will return an error.
///
/// The family of the queue returned is guaranteed to meet the spec of the `QueueFamilySelector` originally used by `find`.
- pub fn get_queue<T: QueueFamilySelector>(&mut self) -> Result<Arc<RwLock<QueueT>>> {
+ pub fn get_queue<T: QueueFamilySelector>(&mut self) -> Result<SharedQueue> {
let tid = TypeId::of::<T>();
let (_, family_id) = self
.family_ids
diff --git a/stockton-skeleton/src/texture/loader.rs b/stockton-skeleton/src/texture/loader.rs
index 80d4a61..6de4a4d 100644
--- a/stockton-skeleton/src/texture/loader.rs
+++ b/stockton-skeleton/src/texture/loader.rs
@@ -12,7 +12,7 @@ use super::{
use crate::{
buffers::image::SampledImage,
context::RenderingContext,
- error::{EnvironmentError, LockPoisoned},
+ error::LockPoisoned,
mem::{MappableBlock, MemoryPool},
queue_negotiator::QueueFamilySelector,
types::*,
@@ -216,22 +216,16 @@ where
config: TextureLoadConfig<R>,
) -> Result<Self> {
// Queue family & Lock
- let family = context
- .queue_negotiator_mut()
- .family::<Q>()
- .ok_or(EnvironmentError::NoSuitableFamilies)?;
- let queue_lock = context.queue_negotiator_mut().get_queue::<Q>()?;
+ let family = context.get_queue_family::<Q>()?;
+ let queue_lock = context.get_queue::<Q>()?;
// Memory pools
let tex_mempool = context.memory_pool()?.clone();
let staging_mempool = context.memory_pool()?.clone();
// Lock device
- let device_lock = context.device().clone();
- let mut device = device_lock
- .write()
- .map_err(|_| LockPoisoned::Device)
- .context("Error getting device lock")?;
+ let device_lock = context.clone_device_lock();
+ let mut device = context.lock_device().context("Error getting device lock")?;
// Physical properties
let device_props = context.physical_device_properties();
diff --git a/stockton-skeleton/src/texture/repo.rs b/stockton-skeleton/src/texture/repo.rs
index 635eebb..4591f17 100644
--- a/stockton-skeleton/src/texture/repo.rs
+++ b/stockton-skeleton/src/texture/repo.rs
@@ -68,11 +68,7 @@ where
// Create Channels
let (req_send, req_recv) = channel();
let (resp_send, resp_recv) = channel();
- let device = context
- .device()
- .write()
- .map_err(|_| LockPoisoned::Device)
- .context("Error getting device lock")?;
+ let device = context.lock_device()?;
// Create descriptor set layout
let ds_lock = Arc::new(RwLock::new(
@@ -193,7 +189,7 @@ where
.unwrap();
// Only now can we lock device without deadlocking
- let mut device = context.device().write().unwrap();
+ let mut device = context.lock_device().unwrap();
// Return all the texture memory and descriptors.
for (_, v) in self.blocks.drain() {
diff --git a/stockton-types/src/components/mod.rs b/stockton-types/src/components/mod.rs
index 421cf9c..a90f5e8 100644
--- a/stockton-types/src/components/mod.rs
+++ b/stockton-types/src/components/mod.rs
@@ -59,17 +59,3 @@ pub struct CameraSettings {
/// Far clipping plane (world units)
pub far: f32,
}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct CameraVPMatrix {
- /// The camera's VP Matrix
- pub vp_matrix: Mat4,
-}
-
-impl Default for CameraVPMatrix {
- fn default() -> Self {
- CameraVPMatrix {
- vp_matrix: Mat4::identity(),
- }
- }
-}