#[cfg(feature = "spirv")]
use alloc::sync::Arc;
use crate::{WgpuCompiledKernel, WgpuCompiler};
use cubecl_core::server::MetadataBindingInfo;
#[cfg(feature = "spirv")]
use cubecl_environment::persistence::Store;
use cubecl_server::compiler::{ArtifactId, CompilationError, CompilationTarget};
#[cfg(feature = "spirv")]
use cubecl_server::compiler::{KernelCacheKey, store_compiled};
use super::artifact::{WgpuArtifact, WgpuArtifactCompiler};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MetadataLayout {
Absent,
Static,
Dynamic,
}
impl From<&MetadataBindingInfo> for MetadataLayout {
fn from(info: &MetadataBindingInfo) -> Self {
if info.data.is_empty() {
Self::Absent
} else if info.dynamic_metadata_offset >= info.data.len() {
Self::Static
} else {
Self::Dynamic
}
}
}
#[derive(Debug)]
pub(crate) struct WgpuPipelines<C> {
compiler: WgpuArtifactCompiler<C>,
#[cfg(feature = "spirv")]
spirv_store: Option<Store<(u64, KernelCacheKey), cubecl_spirv::SpirvCacheEntry>>,
#[cfg(feature = "spirv")]
properties_hash: u64,
#[cfg(feature = "spirv")]
build_id: cubecl_common::hash::StableHash,
}
impl<C> WgpuPipelines<C> {
pub fn new(
compiler: WgpuArtifactCompiler<C>,
#[cfg(feature = "spirv")] spirv_store: Option<
Store<(u64, KernelCacheKey), cubecl_spirv::SpirvCacheEntry>,
>,
#[cfg(feature = "spirv")] properties_hash: u64,
) -> Self {
Self {
compiler,
#[cfg(feature = "spirv")]
spirv_store,
#[cfg(feature = "spirv")]
properties_hash,
#[cfg(feature = "spirv")]
build_id: cubecl_server::compiler::build_id_hash(),
}
}
}
impl<C: WgpuCompiler> CompilationTarget for WgpuPipelines<C> {
type Compiler = WgpuArtifactCompiler<C>;
type Loaded = WgpuCompiledKernel;
fn compiler(&self) -> &WgpuArtifactCompiler<C> {
&self.compiler
}
fn persists(&self) -> bool {
#[cfg(feature = "spirv")]
return self.spirv_store.is_some();
#[cfg(not(feature = "spirv"))]
return false;
}
#[allow(unused_variables)]
fn stored(&mut self, id: &ArtifactId<MetadataLayout>) -> Option<WgpuArtifact> {
#[cfg(not(feature = "spirv"))]
return None;
#[cfg(feature = "spirv")]
{
use crate::{AutoRepresentation, CompilerInfo, ParamsTransfer};
let key = (
self.properties_hash,
KernelCacheKey::new(&id.kernel, self.build_id),
);
let entry = self.spirv_store.as_mut()?.remove(&key)?;
log::trace!("Using SPIR-V cache");
let params_transfer = match entry.kernel.immediate_size {
Some(_) => ParamsTransfer::Immediate,
None => ParamsTransfer::Uniform,
};
Some(WgpuArtifact {
entrypoint_name: entry.entrypoint_name,
source: String::new(),
lang: "spv",
io: entry.kernel.io.clone().map(Arc::from),
repr: Some(AutoRepresentation::SpirV(entry.kernel)),
compiler_info: CompilerInfo::Vulkan { params_transfer },
pipeline: None,
})
}
}
fn load(
&mut self,
id: &ArtifactId<MetadataLayout>,
artifact: &WgpuArtifact,
) -> Result<WgpuCompiledKernel, CompilationError> {
match &artifact.pipeline {
Some(pipeline) => Ok(pipeline.clone()),
None => self.compiler.build(id, artifact),
}
}
#[allow(unused_variables)]
fn store(
&mut self,
id: &ArtifactId<MetadataLayout>,
artifact: WgpuArtifact,
_source: Option<&str>,
) -> bool {
#[cfg(not(feature = "spirv"))]
return false;
#[cfg(feature = "spirv")]
{
let (Some(store), Some(crate::AutoRepresentation::SpirV(kernel))) =
(self.spirv_store.as_mut(), artifact.repr)
else {
return false;
};
let key = (
self.properties_hash,
KernelCacheKey::new(&id.kernel, self.build_id),
);
store_compiled(
store,
key,
cubecl_spirv::SpirvCacheEntry::new(artifact.entrypoint_name, kernel),
)
}
}
}