vidya/crates/renderer_device/src/pipeline_cache.rs

111 lines
3.4 KiB
Rust

use ash::vk;
use ash::Device;
use crate::PhysicalDeviceInfo;
#[derive(Debug, Clone, Copy)]
pub struct PipelineCache {
#[allow(dead_code)]
key: u128,
pub raw: vk::PipelineCache,
}
impl PipelineCache {
pub(crate) unsafe fn destroy(self, device: &ash::Device) {
tracing::info!("destroying pipeline cache with key {:x}", self.key);
if let Ok(data) = self.export(device) {
tracing::info!(
"exported pipeline cache with key {:x} and size {} bytes",
self.key,
data.len()
);
_ = Self::write_to_disk(self.key, &data).inspect_err(|err| {
tracing::error!("failed to write pipeline cache to disk: {err}");
});
}
unsafe { device.destroy_pipeline_cache(self.raw, None) };
}
const MAGIC: [u8; 4] = *b"VYPC";
const KEY_VERSION: u32 = 1;
const PATH: &'static str = "pipeline_cache.bin";
fn calculate_key(adapter: &PhysicalDeviceInfo) -> u128 {
use md5::Digest;
let mut hasher = md5::Md5::new();
let props = &adapter.properties;
hasher.update(bytemuck::bytes_of(&[
props.core.vendor_id,
props.core.api_version,
props.core.device_id,
props.core.driver_version,
]));
u128::from_le_bytes(hasher.finalize().into())
}
fn load_from_disk(key: u128) -> Option<(u128, Vec<u8>)> {
use std::io::Read;
let file = std::fs::File::open(Self::PATH).ok()?;
let mut reader = std::io::BufReader::new(file);
let mut magic = [0; 4];
reader.read_exact(&mut magic).ok()?;
if magic != Self::MAGIC {
return None;
}
let mut version = 0;
reader
.read_exact(bytemuck::bytes_of_mut(&mut version))
.ok()?;
if version != Self::KEY_VERSION {
return None;
}
let mut disk_key = 0;
reader
.read_exact(bytemuck::bytes_of_mut(&mut disk_key))
.ok()?;
if disk_key != key {
return None;
}
let mut data = Vec::new();
reader.read_to_end(&mut data).ok()?;
Some((key, data))
}
fn write_to_disk(key: u128, data: &[u8]) -> std::io::Result<()> {
use std::io::Write;
let file = std::fs::File::create(Self::PATH)?;
let mut writer = std::io::BufWriter::new(file);
writer.write_all(&Self::MAGIC)?;
writer.write_all(bytemuck::bytes_of(&Self::KEY_VERSION))?;
writer.write_all(bytemuck::bytes_of(&key))?;
writer.write_all(data)?;
Ok(())
}
pub fn new(device: &Device, adapter: &PhysicalDeviceInfo) -> crate::Result<Self> {
let key = Self::calculate_key(adapter);
let data = Self::load_from_disk(key).map(|(key, data)| {
tracing::info!("loaded pipeline cache from disk with key {key:x}");
data
});
let info = vk::PipelineCacheCreateInfo::default()
// .flags(vk::PipelineCacheCreateFlags::EXTERNALLY_SYNCHRONIZED)
.initial_data(data.as_deref().unwrap_or_default());
let cache = unsafe { device.create_pipeline_cache(&info, None)? };
Ok(Self { key, raw: cache })
}
pub fn export(&self, device: &ash::Device) -> crate::Result<Vec<u8>> {
let data = unsafe { device.get_pipeline_cache_data(self.raw)? };
Ok(data)
}
}