#![no_std]
#![deny(missing_docs)]
#![deny(clippy::disallowed_methods, clippy::disallowed_types)]
#![cfg_attr(
not(test),
deny(
clippy::indexing_slicing,
clippy::arithmetic_side_effects,
clippy::cast_possible_truncation
)
)]
extern crate alloc;
mod io;
mod lower;
mod profile;
use alloc::{format, string::String, vec::Vec};
use core::fmt;
use iridia_ir::interface::{D3dClass, Interface, ResourceId, ResourceKind};
pub use io::{EntryIo, Item, entry_io};
pub use lower::module;
pub use profile::{Profile, Requirement, preflight, requirements};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Error(pub String);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl core::error::Error for Error {}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Bind {
pub resource: ResourceId,
pub group: u32,
pub binding: u32,
pub name: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct BindingPlan {
pub bindings: Vec<Bind>,
}
impl BindingPlan {
pub fn d3d(interface: &Interface) -> Result<BindingPlan, Error> {
let mut bindings = Vec::new();
for r in interface
.resources
.iter()
.filter(|r| !matches!(r.kind, ResourceKind::PipelineState(_)))
{
let d = r.provenance.d3d.as_ref().ok_or_else(|| {
Error(format!(
"resource {} ({}) has no D3D register",
r.id.0, r.provenance.name
))
})?;
let (group, prefix) = match d.class {
D3dClass::ConstantBuffer => (0, "cb"),
D3dClass::ShaderResource => (1, "t"),
D3dClass::Sampler => (2, "s"),
D3dClass::UnorderedAccess => (3, "u"),
};
if d.space != 0 {
return Err(Error(format!(
"resource {} is in register space {}",
r.provenance.name, d.space
)));
}
bindings.push(Bind {
resource: r.id,
group,
binding: d.register,
name: format!("{prefix}{}", d.register),
});
}
Ok(BindingPlan { bindings })
}
}
pub fn validate(module: &naga::Module, profile: &Profile) -> Result<naga::valid::ModuleInfo, Error> {
naga::valid::Validator::new(naga::valid::ValidationFlags::all(), profile.capabilities)
.validate(module)
.map_err(|e| Error(format!("naga validation: {}", e.as_inner())))
}
#[cfg(feature = "wgsl")]
pub fn wgsl(program: &iridia_ir::Program, profile: &Profile) -> Result<String, Error> {
preflight(program, profile)?;
let plan = BindingPlan::d3d(&program.interface)?;
let module = module(program, &plan)?;
let info = validate(&module, profile).map_err(|e| {
Error(format!(
"lowering bug: the preflight accepted the program and {}",
e.0
))
})?;
naga::back::wgsl::write_string(&module, &info, naga::back::wgsl::WriterFlags::empty())
.map_err(|e| Error(format!("naga's WGSL writer: {e}")))
}
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
struct ReadmeExamples;