aboutsummaryrefslogtreecommitdiff
path: root/stockton-skeleton/src/context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'stockton-skeleton/src/context.rs')
-rw-r--r--stockton-skeleton/src/context.rs275
1 files changed, 275 insertions, 0 deletions
diff --git a/stockton-skeleton/src/context.rs b/stockton-skeleton/src/context.rs
new file mode 100644
index 0000000..802b8ca
--- /dev/null
+++ b/stockton-skeleton/src/context.rs
@@ -0,0 +1,275 @@
+//! Deals with all the Vulkan/HAL details.
+//! This relies on draw passes for the actual drawing logic.
+
+use std::{
+ mem::ManuallyDrop,
+ ptr::read,
+ sync::{Arc, RwLock},
+};
+
+use anyhow::{Context, Result};
+use hal::pool::CommandPoolCreateFlags;
+use log::debug;
+
+use winit::window::Window;
+
+use super::{
+ draw_passes::{DrawPass, IntoDrawPass},
+ queue_negotiator::{DrawQueue, QueueNegotiator},
+ target::{SwapchainProperties, TargetChain},
+};
+use crate::{
+ error::{EnvironmentError, LockPoisoned},
+ types::*,
+};
+
+use stockton_types::Session;
+
+/// 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 {
+ // Parents for most of these things
+ /// Vulkan Instance
+ instance: ManuallyDrop<back::Instance>,
+
+ /// Device we're using
+ device: Arc<RwLock<DeviceT>>,
+
+ /// Adapter we're using
+ adapter: Adapter,
+
+ /// Swapchain and stuff
+ target_chain: ManuallyDrop<TargetChain>,
+
+ // Command pool and buffers
+ /// The command pool used for our buffers
+ cmd_pool: ManuallyDrop<CommandPoolT>,
+
+ queue_negotiator: QueueNegotiator,
+
+ /// The queue to use for drawing
+ queue: Arc<RwLock<QueueT>>,
+
+ pixels_per_point: f32,
+}
+
+impl RenderingContext {
+ /// Create a new RenderingContext for the given window.
+ pub fn new<IDP: IntoDrawPass<DP>, DP: DrawPass>(window: &Window) -> Result<Self> {
+ // Create surface
+ let (instance, surface, mut adapters) = unsafe {
+ let instance =
+ back::Instance::create("stockton", 1).context("Error creating vulkan instance")?;
+ let surface = instance
+ .create_surface(window)
+ .context("Error creating surface")?;
+ let adapters = instance.enumerate_adapters();
+
+ (instance, surface, adapters)
+ };
+
+ // TODO: Properly figure out which adapter to use
+ let adapter = adapters.remove(0);
+
+ // Queue Negotiator
+ let mut queue_families_specs = Vec::new();
+ let (mut queue_negotiator, surface) = {
+ let dq: DrawQueue = DrawQueue { surface };
+
+ let mut qn = QueueNegotiator::default();
+
+ // Draw Queue
+ qn.find(&adapter, &dq)
+ .context("Couldn't find draw queue family")?;
+ queue_families_specs.push(
+ qn.family_spec::<DrawQueue>(&adapter.queue_families, 1)
+ .context("Couldn't find draw queue family")?,
+ );
+
+ // Auxiliary queues for DP
+ queue_families_specs.extend(
+ IDP::find_aux_queues(&adapter, &mut qn)
+ .context("Level pass couldn't populate queue negotiator")?,
+ );
+
+ (qn, dq.surface)
+ };
+
+ // Device & Queue groups
+ let (device_lock, queue_groups) = {
+ // TODO: This sucks, but hal is restrictive on how we can pass this specific argument.
+ let queue_families_specs_real: Vec<_> = queue_families_specs
+ .iter()
+ .map(|(qf, ns)| (*qf, ns.as_slice()))
+ .collect();
+
+ let gpu = unsafe {
+ adapter
+ .physical_device
+ .open(queue_families_specs_real.as_slice(), hal::Features::empty())
+ .context("Error opening logical device")?
+ };
+
+ (Arc::new(RwLock::new(gpu.device)), gpu.queue_groups)
+ };
+
+ queue_negotiator.set_queue_groups(queue_groups);
+
+ // Figure out what our swapchain will look like
+ let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface)
+ .context("Error getting properties for swapchain")?;
+
+ // Lock device
+ let mut device = device_lock
+ .write()
+ .map_err(|_| LockPoisoned::Device)
+ .context("Error getting device lock")?;
+
+ debug!("Detected swapchain properties: {:?}", swapchain_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,
+ &adapter,
+ surface,
+ &mut cmd_pool,
+ swapchain_properties,
+ )
+ .context("Error creating target chain")?;
+
+ // Unlock device
+ drop(device);
+
+ let queue = queue_negotiator
+ .get_queue::<DrawQueue>()
+ .ok_or(EnvironmentError::NoQueues)
+ .context("Error getting draw queue")?;
+
+ Ok(RenderingContext {
+ instance: ManuallyDrop::new(instance),
+
+ device: device_lock,
+ adapter,
+
+ queue_negotiator,
+ queue,
+
+ target_chain: ManuallyDrop::new(target_chain),
+ cmd_pool: ManuallyDrop::new(cmd_pool),
+
+ // pixels_per_point: window.scale_factor() as f32,
+ pixels_per_point: window.scale_factor() as f32,
+ })
+ }
+
+ /// 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);
+
+ let properties = SwapchainProperties::find_best(&self.adapter, &surface)
+ .context("Error finding best swapchain properties")?;
+
+ self.target_chain = ManuallyDrop::new(
+ TargetChain::new(
+ &mut device,
+ &self.adapter,
+ surface,
+ &mut self.cmd_pool,
+ properties,
+ )
+ .context("Error creating target chain")?,
+ );
+ Ok(())
+ }
+
+ /// Draw onto the next frame of the swapchain
+ pub fn draw_next_frame<DP: DrawPass>(&mut self, session: &Session, dp: &mut DP) -> Result<()> {
+ let mut device = self
+ .device
+ .write()
+ .map_err(|_| LockPoisoned::Device)
+ .context("Error getting device lock")?;
+ let mut queue = self
+ .queue
+ .write()
+ .map_err(|_| LockPoisoned::Queue)
+ .context("Error getting draw queue lock")?;
+
+ // Level draw pass
+ self.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
+ }
+}
+
+impl core::ops::Drop for RenderingContext {
+ fn drop(&mut self) {
+ {
+ self.device.write().unwrap().wait_idle().unwrap();
+ }
+
+ unsafe {
+ let mut device = self.device.write().unwrap();
+
+ ManuallyDrop::into_inner(read(&self.target_chain)).deactivate(
+ &mut self.instance,
+ &mut device,
+ &mut self.cmd_pool,
+ );
+
+ device.destroy_command_pool(ManuallyDrop::into_inner(read(&self.cmd_pool)));
+ }
+ }
+}