mod build;
#[cfg(test)]
mod tests;
use concinnity_core::render::ops::RenderOps;
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use crate::gfx::system::parked::ShaderOverrides;
use build::{BuildRequest, Built};
pub(crate) use build::ShaderPayloadSource;
pub(crate) struct DeferredBucket {
pub bucket: u32,
pub source: ShaderPayloadSource,
}
struct Entry {
bucket: u32,
source: Arc<ShaderPayloadSource>,
blocked: bool,
resident: bool,
building: bool,
}
impl Entry {
fn wants_install(&self) -> bool {
!self.blocked && !self.resident && !self.building
}
fn wants_evict(&self) -> bool {
self.blocked && self.resident
}
}
pub(crate) struct ShaderWarmup {
entries: Vec<Entry>,
overrides: Option<ShaderOverrides>,
built_tx: Sender<Built>,
built_rx: Receiver<Built>,
}
impl ShaderWarmup {
pub(crate) fn new(deferred: Vec<DeferredBucket>, overrides: Option<ShaderOverrides>) -> Self {
let (built_tx, built_rx) = channel();
Self {
entries: deferred
.into_iter()
.map(|d| Entry {
bucket: d.bucket,
source: Arc::new(d.source),
blocked: true,
resident: false,
building: false,
})
.collect(),
overrides,
built_tx,
built_rx,
}
}
pub(crate) fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub(crate) fn set_blocked(&mut self, bucket: u32, blocked: bool) {
if let Some(e) = self.entries.iter_mut().find(|e| e.bucket == bucket) {
e.blocked = blocked;
}
}
pub(crate) fn pump(&mut self, ops: &mut RenderOps, mut on_resident: impl FnMut(u32, bool)) {
while let Ok(built) = self.built_rx.try_recv() {
if let Some(bucket) = self.finish(built, ops) {
on_resident(bucket, true);
}
}
for i in 0..self.entries.len() {
let entry = &self.entries[i];
if entry.wants_install() {
self.dispatch(i, ops);
} else if entry.wants_evict() {
let bucket = entry.bucket;
ops.record(move |backend| {
backend.evict_world_shader(bucket);
tracing::info!(
"StreamingSystem: shader bucket {} pipeline released",
bucket
);
});
self.entries[i].resident = false;
on_resident(bucket, false);
}
}
}
fn dispatch(&mut self, i: usize, ops: &mut RenderOps) {
let entry = &mut self.entries[i];
entry.building = true;
let request = BuildRequest {
bucket: entry.bucket,
source: Arc::clone(&entry.source),
overrides: self.overrides.clone(),
done: self.built_tx.clone(),
};
ops.record(move |backend| request.spawn(backend.pipeline_builder()));
}
fn finish(&mut self, built: Built, ops: &mut RenderOps) -> Option<u32> {
let i = self.entries.iter().position(|e| e.bucket == built.bucket)?;
self.entries[i].building = false;
if self.entries[i].blocked {
return None;
}
let bucket = built.bucket;
match built.outcome {
Err(e) => tracing::error!(
"StreamingSystem: shader bucket {} could not be built: {}",
bucket,
e
),
Ok(shader) if !shader.is_current() => {
self.dispatch(i, ops);
return None;
}
Ok(shader) => match shader.ready() {
Ok(ready) => ops.record(move |backend| ready.install(backend)),
Err(e) => tracing::error!(
"StreamingSystem: shader bucket {} pipeline build failed: {}",
bucket,
e
),
},
}
self.entries[i].resident = true;
Some(bucket)
}
}