use alloc::{sync::Arc, vec::Vec};
use smallvec::SmallVec;
use thiserror::Error;
use crate::{
device::{
queue::{EncoderInFlight, SubmittedWorkDoneClosure, TempResource},
DeviceError,
},
ray_tracing::BlasCompactReadyPendingClosure,
resource::{Blas, Buffer, Texture, Trackable},
snatch::SnatchGuard,
SubmissionIndex,
};
struct ActiveSubmission {
index: SubmissionIndex,
mapped: Vec<Arc<Buffer>>,
compact_read_back: Vec<Arc<Blas>>,
encoders: Vec<EncoderInFlight>,
work_done_closures: SmallVec<[SubmittedWorkDoneClosure; 1]>,
}
impl ActiveSubmission {
pub fn contains_buffer(&self, buffer: &Buffer) -> bool {
for encoder in &self.encoders {
if encoder.trackers.buffers.contains(buffer) {
return true;
}
if encoder
.pending_buffers
.contains_key(&buffer.tracker_index())
{
return true;
}
}
false
}
pub fn contains_texture(&self, texture: &Texture) -> bool {
for encoder in &self.encoders {
if encoder.trackers.textures.contains(texture) {
return true;
}
if encoder
.pending_textures
.contains_key(&texture.tracker_index())
{
return true;
}
}
false
}
pub fn contains_blas(&self, blas: &Blas) -> bool {
for encoder in &self.encoders {
if encoder.trackers.blas_s.contains(blas) {
return true;
}
if encoder.pending_blas_s.contains_key(&blas.tracker_index()) {
return true;
}
}
false
}
}
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum WaitIdleError {
#[error(transparent)]
Device(#[from] DeviceError),
#[error("Tried to wait using a submission index ({0}) that has not been returned by a successful submission (last successful submission: {1})")]
WrongSubmissionIndex(SubmissionIndex, SubmissionIndex),
#[error("Timed out trying to wait for the given submission index.")]
Timeout,
}
impl WaitIdleError {
pub fn to_poll_error(&self) -> Option<wgt::PollError> {
match self {
WaitIdleError::Timeout => Some(wgt::PollError::Timeout),
&WaitIdleError::WrongSubmissionIndex(a, b) => {
Some(wgt::PollError::WrongSubmissionIndex(a, b))
}
_ => None,
}
}
}
pub(crate) struct LifetimeTracker {
active: Vec<ActiveSubmission>,
ready_to_map: Vec<Arc<Buffer>>,
ready_to_compact: Vec<Arc<Blas>>,
work_done_closures: SmallVec<[SubmittedWorkDoneClosure; 1]>,
}
impl LifetimeTracker {
pub fn new() -> Self {
Self {
active: Vec::new(),
ready_to_map: Vec::new(),
ready_to_compact: Vec::new(),
work_done_closures: SmallVec::new(),
}
}
pub fn queue_empty(&self) -> bool {
self.active.is_empty()
}
pub fn track_submission(&mut self, index: SubmissionIndex, encoders: Vec<EncoderInFlight>) {
self.active.push(ActiveSubmission {
index,
mapped: Vec::new(),
compact_read_back: Vec::new(),
encoders,
work_done_closures: SmallVec::new(),
});
}
pub(crate) fn map(&mut self, buffer: &Arc<Buffer>) -> Option<SubmissionIndex> {
let submission = self
.active
.iter_mut()
.rev()
.find(|a| a.contains_buffer(buffer));
let maybe_submission_index = submission.as_ref().map(|s| s.index);
submission
.map_or(&mut self.ready_to_map, |a| &mut a.mapped)
.push(buffer.clone());
maybe_submission_index
}
pub(crate) fn prepare_compact(&mut self, blas: &Arc<Blas>) -> Option<SubmissionIndex> {
let submission = self.active.iter_mut().rev().find(|a| a.contains_blas(blas));
let maybe_submission_index = submission.as_ref().map(|s| s.index);
submission
.map_or(&mut self.ready_to_compact, |a| &mut a.compact_read_back)
.push(blas.clone());
maybe_submission_index
}
pub fn get_buffer_latest_submission_index(&self, buffer: &Buffer) -> Option<SubmissionIndex> {
self.active.iter().rev().find_map(|submission| {
if submission.contains_buffer(buffer) {
Some(submission.index)
} else {
None
}
})
}
pub fn get_texture_latest_submission_index(
&self,
texture: &Texture,
) -> Option<SubmissionIndex> {
self.active.iter().rev().find_map(|submission| {
if submission.contains_texture(texture) {
Some(submission.index)
} else {
None
}
})
}
#[must_use]
pub fn triage_submissions(
&mut self,
last_done: SubmissionIndex,
) -> SmallVec<[SubmittedWorkDoneClosure; 1]> {
profiling::scope!("triage_submissions");
let done_count = self
.active
.iter()
.position(|a| a.index > last_done)
.unwrap_or(self.active.len());
let mut work_done_closures: SmallVec<_> = self.work_done_closures.drain(..).collect();
for a in self.active.drain(..done_count) {
self.ready_to_map.extend(a.mapped);
self.ready_to_compact.extend(a.compact_read_back);
for encoder in a.encoders {
profiling::scope!("drop command buffer trackers");
drop(encoder);
}
work_done_closures.extend(a.work_done_closures);
}
work_done_closures
}
pub fn schedule_resource_destruction(
&mut self,
temp_resource: TempResource,
last_submit_index: SubmissionIndex,
) {
let resources = self
.active
.iter_mut()
.find(|a| a.index == last_submit_index)
.map(|a| {
&mut a.encoders.last_mut().unwrap().temp_resources
});
if let Some(resources) = resources {
resources.push(temp_resource);
}
}
pub fn add_work_done_closure(
&mut self,
closure: SubmittedWorkDoneClosure,
) -> Option<SubmissionIndex> {
match self.active.last_mut() {
Some(active) => {
active.work_done_closures.push(closure);
Some(active.index)
}
None => {
self.work_done_closures.push(closure);
None
}
}
}
#[must_use]
pub(crate) fn handle_mapping(
&mut self,
snatch_guard: &SnatchGuard,
) -> Vec<super::BufferMapPendingClosure> {
if self.ready_to_map.is_empty() {
return Vec::new();
}
let mut pending_callbacks: Vec<super::BufferMapPendingClosure> =
Vec::with_capacity(self.ready_to_map.len());
for buffer in self.ready_to_map.drain(..) {
match buffer.map(snatch_guard) {
Some(cb) => pending_callbacks.push(cb),
None => continue,
}
}
pending_callbacks
}
#[must_use]
pub(crate) fn handle_compact_read_back(&mut self) -> Vec<BlasCompactReadyPendingClosure> {
if self.ready_to_compact.is_empty() {
return Vec::new();
}
let mut pending_callbacks: Vec<BlasCompactReadyPendingClosure> =
Vec::with_capacity(self.ready_to_compact.len());
for blas in self.ready_to_compact.drain(..) {
match blas.read_back_compact_size() {
Some(cb) => pending_callbacks.push(cb),
None => continue,
}
}
pending_callbacks
}
}