aboutsummaryrefslogtreecommitdiff
path: root/stockton-skeleton/src/queue_negotiator.rs
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:24 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:24 +0100
commit0ddc1e39dc24cff636defbbbab974967bb5301b8 (patch)
tree7745054a2bc4030819eed3d39e0df45c94cc8b40 /stockton-skeleton/src/queue_negotiator.rs
parentb86e97f67a07368877bd18501aebcbe80cf93118 (diff)
fix(queues): fix validation errors & docs
Diffstat (limited to 'stockton-skeleton/src/queue_negotiator.rs')
-rw-r--r--stockton-skeleton/src/queue_negotiator.rs121
1 files changed, 90 insertions, 31 deletions
diff --git a/stockton-skeleton/src/queue_negotiator.rs b/stockton-skeleton/src/queue_negotiator.rs
index 879a935..f3e38fd 100644
--- a/stockton-skeleton/src/queue_negotiator.rs
+++ b/stockton-skeleton/src/queue_negotiator.rs
@@ -1,6 +1,40 @@
-use crate::{error::EnvironmentError, types::*};
+//! Used for requesting appropriate queue families, and sharing/allocating queues as necessary.
+//! This is created by `RenderingContext`, and should mostly be accessed from [`crate::draw_passes::IntoDrawPass`].
+//!
+//! For example, to use a `TexLoadQueue` in your drawpass:
+//! ```
+//! # use crate::{types::*, texture::TexLoadQueue};
+//! fn find_aux_queues<'c>(
+//! adapter: &'c Adapter,
+//! queue_negotiator: &mut QueueNegotiator,
+//! ) -> Result<Vec<(&'c QueueFamilyT, Vec<f32>)>> {
+//! queue_negotiator.find(adapter, &TexLoadQueue)?;
+//!
+//! Ok(vec![queue_negotiator
+//! .family_spec::<TexLoadQueue>(&adapter.queue_families, 1)
+//! .ok_or(EnvironmentError::NoSuitableFamilies)?])
+//! }
+//! ```
+//!
+//! Then get your queue in [`crate::draw_passes::IntoDrawPass::init`]
+//! ```
+//! # use crate::{types::*, context::RenderingContext, texture::TexLoadQueue};
+//! # use stockton_types::Session;
+//! fn init(
+//! self,
+//! session: &mut Session,
+//! context: &mut RenderingContext,
+//! ) -> Result<LevelDrawPass<'a, M>> {
+//! let queue = context.queue_negotiator_mut().get_queue::<TexLoadQueue>().unwrap();
+//! }
+//! ```
+
+use crate::{
+ error::{EnvironmentError, UsageError},
+ types::*,
+};
-use anyhow::{Error, Result};
+use anyhow::{bail, Error, Result};
use hal::queue::family::QueueFamilyId;
use std::{
any::TypeId,
@@ -8,7 +42,8 @@ use std::{
sync::{Arc, RwLock},
};
-type SharedQueue = Arc<RwLock<QueueT>>;
+/// A queue, possibly shared between threads.
+pub type SharedQueue = Arc<RwLock<QueueT>>;
/// Used to find appropriate queue families and share queues from them as needed.
pub struct QueueNegotiator {
@@ -17,13 +52,16 @@ pub struct QueueNegotiator {
all: Vec<QueueGroup>,
}
-/// Can be used to select a specific queue family
+/// Can be used to select an appropriate queue family
pub trait QueueFamilySelector: 'static {
- /// Check if the given family is suitable
+ /// Return true if the given family is suitable
fn is_suitable(&self, family: &QueueFamilyT) -> bool;
}
impl QueueNegotiator {
+ /// Attempt to find an appropriate queue family using the given selector.
+ /// Returns early if the *type* of the selector has already been allocated a family.
+ /// This should usually be called by [`crate::draw_passes::IntoDrawPass::find_aux_queues`].
pub fn find<T: QueueFamilySelector>(&mut self, adapter: &Adapter, filter: &T) -> Result<()> {
if self.family_ids.contains_key(&TypeId::of::<T>()) {
return Ok(());
@@ -53,46 +91,79 @@ impl QueueNegotiator {
Ok(())
}
- pub fn set_queue_groups(&mut self, queue_groups: Vec<QueueGroup>) {
- self.all = queue_groups
- }
-
- pub fn get_queue<T: QueueFamilySelector>(&mut self) -> Option<Arc<RwLock<QueueT>>> {
+ /// Get a (possibly shared) queue. You should prefer to call this once and store the result.
+ /// You should already have called [`self::QueueNegotiator::find`] and [`self::QueueNegotiator::family_spec`],
+ /// otherwise this will return an error.
+ ///
+ /// Round-robin allocation is used to try to fairly distribute work between each queue.
+ /// 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>>> {
let tid = TypeId::of::<T>();
- let family_id = self.family_ids.get(&tid)?;
- log::debug!("{:?}", self.all);
- log::debug!("{:?}", self.already_allocated);
+ let family_id = self
+ .family_ids
+ .get(&tid)
+ .ok_or(UsageError::QueueNegotiatorMisuse)?;
+
match self
.all
.iter()
.position(|x| !x.queues.is_empty() && x.family == *family_id)
{
Some(idx) => {
- // At least one remaining queue
+ // At least one remaining unused queue
let queue = self.all[idx].queues.pop().unwrap();
let queue = Arc::new(RwLock::new(queue));
self.add_to_allocated::<T>(queue.clone());
- Some(queue)
+ Ok(queue)
}
None => match self.already_allocated.get_mut(&tid) {
Some((queues, next_share)) => {
let queue = (&queues[*next_share]).clone();
- *next_share += 1;
+ *next_share = (*next_share + 1) % queues.len();
- Some(queue)
+ Ok(queue)
}
- None => None,
+ None => bail!(EnvironmentError::NoQueues),
},
}
}
+ /// Convenience function to get a queue spec for the given selector.
+ /// You should probably call this from [`crate::draw_passes::IntoDrawPass::find_aux_queues`].
+ /// `count` is the maximum number of individual queues to request. You may get less than this, in which case they will be shared.
+ /// This will return an error if you haven't called [`self::QueueNegotiator::find`] beforehand, or if there were no suitable queue families.
+ pub fn family_spec<'a, T: QueueFamilySelector>(
+ &self,
+ queue_families: &'a [QueueFamilyT],
+ count: usize,
+ ) -> Result<(&'a QueueFamilyT, Vec<f32>)> {
+ let qf_id = self
+ .family::<T>()
+ .ok_or(UsageError::QueueNegotiatorMisuse)?;
+
+ let qf = queue_families
+ .iter()
+ .find(|x| x.id() == qf_id)
+ .ok_or(EnvironmentError::NoSuitableFamilies)?;
+ let v = vec![1.0; count];
+
+ Ok((qf, v))
+ }
+
+ /// Get the queue family ID being used by the given selector
pub fn family<T: QueueFamilySelector>(&self) -> Option<QueueFamilyId> {
self.family_ids.get(&TypeId::of::<T>()).cloned()
}
+ /// Used internally to get the queue groups from the adapter.
+ pub(crate) fn set_queue_groups(&mut self, queue_groups: Vec<QueueGroup>) {
+ self.all = queue_groups
+ }
+
+ /// Used internally to mark that we've started sharing a queue
fn add_to_allocated<T: QueueFamilySelector>(&mut self, queue: Arc<RwLock<QueueT>>) {
let tid = TypeId::of::<T>();
match self.already_allocated.get_mut(&tid) {
@@ -104,19 +175,6 @@ impl QueueNegotiator {
}
}
}
-
- pub fn family_spec<'a, T: QueueFamilySelector>(
- &self,
- queue_families: &'a [QueueFamilyT],
- count: usize,
- ) -> Option<(&'a QueueFamilyT, Vec<f32>)> {
- let qf_id = self.family::<T>()?;
-
- let qf = queue_families.iter().find(|x| x.id() == qf_id)?;
- let v = vec![1.0; count];
-
- Some((qf, v))
- }
}
impl Default for QueueNegotiator {
@@ -129,6 +187,7 @@ impl Default for QueueNegotiator {
}
}
+/// A queue suitable for drawing to a given surface with.
pub struct DrawQueue {
pub surface: SurfaceT,
}