#[derive(Clone, Debug, PartialEq)]
pub(crate) struct ShaderPipelineRecord {
pub(crate) source: u64,
pub(crate) overrides: u64,
pub(crate) mode: u8,
pub(crate) variant: u8,
pub(crate) split: Option<String>,
pub(crate) constants: Vec<(String, f64)>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct Recorded<T> {
pub(crate) entry: T,
pub(crate) age: u8,
pub(crate) first_screen: bool,
}
impl<T> Recorded<T> {
pub(crate) fn as_ref(&self) -> Recorded<&T> {
Recorded {
entry: &self.entry,
age: self.age,
first_screen: self.first_screen,
}
}
pub(crate) fn map<U>(self, f: impl FnOnce(T) -> U) -> Recorded<U> {
Recorded {
entry: f(self.entry),
age: self.age,
first_screen: self.first_screen,
}
}
}
#[derive(Default)]
pub(crate) struct PipelineRecords {
pub(crate) shapes: Vec<Recorded<u64>>,
pub(crate) shaders: Vec<Recorded<ShaderPipelineRecord>>,
pub(crate) fixed: Vec<Recorded<String>>,
}
const SECTION: &[u8; 4] = b"RSP3";
pub(crate) fn encode<'a>(
shapes: impl Iterator<Item = Recorded<u64>>,
shaders: impl Iterator<Item = Recorded<&'a ShaderPipelineRecord>>,
fixed: impl Iterator<Item = Recorded<&'a str>>,
bytes: &mut Vec<u8>,
) -> Option<()> {
bytes.extend_from_slice(SECTION);
put_list(bytes, shapes, |bytes, key| {
bytes.extend_from_slice(&key.to_le_bytes());
Some(())
})?;
put_list(bytes, shaders, |bytes, record| {
bytes.extend_from_slice(&record.source.to_le_bytes());
bytes.extend_from_slice(&record.overrides.to_le_bytes());
bytes.extend_from_slice(&[record.mode, record.variant]);
put_name(bytes, record.split.as_deref().unwrap_or_default())?;
bytes.extend_from_slice(&u16::try_from(record.constants.len()).ok()?.to_le_bytes());
for (name, value) in &record.constants {
put_name(bytes, name)?;
bytes.extend_from_slice(&value.to_le_bytes());
}
Some(())
})?;
put_list(bytes, fixed, put_name)
}
fn put_list<T>(
bytes: &mut Vec<u8>,
records: impl Iterator<Item = Recorded<T>>,
mut put: impl FnMut(&mut Vec<u8>, T) -> Option<()>,
) -> Option<()> {
let count_at = bytes.len();
bytes.extend_from_slice(&[0; 4]);
let mut count = 0_u32;
for record in records {
put(bytes, record.entry)?;
let age = (record.age < 0x80).then_some(record.age)?;
bytes.push(age << 1 | u8::from(record.first_screen));
count = count.checked_add(1)?;
}
bytes
.get_mut(count_at..count_at + 4)?
.copy_from_slice(&count.to_le_bytes());
Some(())
}
fn put_name(bytes: &mut Vec<u8>, name: &str) -> Option<()> {
bytes.push(u8::try_from(name.len()).ok()?);
bytes.extend_from_slice(name.as_bytes());
Some(())
}
pub(crate) fn decode(bytes: &[u8]) -> Option<(PipelineRecords, &[u8])> {
let mut reader = Reader(bytes.strip_prefix(SECTION.as_slice())?);
let shapes = reader.list(Reader::u64)?;
let shaders = reader.list(|reader| {
let source = reader.u64()?;
let overrides = reader.u64()?;
let [mode, variant] = reader.take()?;
let split = Some(reader.name()?).filter(|split| !split.is_empty());
let constants = (0..reader.u16()?)
.map(|_| Some((reader.name()?, f64::from_le_bytes(reader.take()?))))
.collect::<Option<Vec<_>>>()?;
Some(ShaderPipelineRecord {
source,
overrides,
mode,
variant,
split,
constants,
})
})?;
let fixed = reader.list(Reader::name)?;
Some((
PipelineRecords {
shapes,
shaders,
fixed,
},
reader.0,
))
}
struct Reader<'a>(&'a [u8]);
impl Reader<'_> {
fn take<const N: usize>(&mut self) -> Option<[u8; N]> {
let (head, rest) = self.0.split_first_chunk::<N>()?;
self.0 = rest;
Some(*head)
}
fn u16(&mut self) -> Option<u16> {
self.take().map(u16::from_le_bytes)
}
fn u32(&mut self) -> Option<u32> {
self.take().map(u32::from_le_bytes)
}
fn u64(&mut self) -> Option<u64> {
self.take().map(u64::from_le_bytes)
}
fn list<T>(
&mut self,
mut entry: impl FnMut(&mut Self) -> Option<T>,
) -> Option<Vec<Recorded<T>>> {
(0..self.u32()?)
.map(|_| {
let entry = entry(self)?;
let [mark] = self.take()?;
Some(Recorded {
entry,
age: mark >> 1,
first_screen: mark & 1 == 1,
})
})
.collect()
}
fn name(&mut self) -> Option<String> {
let [len] = self.take()?;
let (name, rest) = self.0.split_at_checked(usize::from(len))?;
self.0 = rest;
String::from_utf8(name.to_vec()).ok()
}
}