#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TierCaps {
pub downlevel_flags: wgpu::DownlevelFlags,
pub adapter_name: String,
}
pub const ENGINE_REQUIRED_DOWNLEVEL_FLAGS: wgpu::DownlevelFlags = wgpu::DownlevelFlags::empty();
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EngineUnsupported {
pub adapter_name: String,
pub missing_flags: wgpu::DownlevelFlags,
}
impl std::fmt::Display for EngineUnsupported {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"frust-render: adapter `{}` cannot drive the frust-engine renderer — it lacks the \
required downlevel flags ({:?}; the engine requires \
{ENGINE_REQUIRED_DOWNLEVEL_FLAGS:?}), and this build contains no other renderer to \
fall back to.",
self.adapter_name, self.missing_flags,
)
}
}
impl std::error::Error for EngineUnsupported {}
fn engine_support_with(
required: wgpu::DownlevelFlags,
caps: &TierCaps,
) -> Result<(), EngineUnsupported> {
let missing_flags = required - caps.downlevel_flags;
if missing_flags.is_empty() {
return Ok(());
}
Err(EngineUnsupported {
adapter_name: caps.adapter_name.clone(),
missing_flags,
})
}
pub fn engine_support(caps: &TierCaps) -> Result<(), EngineUnsupported> {
engine_support_with(ENGINE_REQUIRED_DOWNLEVEL_FLAGS, caps)
}
#[cfg(test)]
mod tests {
use super::*;
fn caps(flags: wgpu::DownlevelFlags) -> TierCaps {
TierCaps {
downlevel_flags: flags,
adapter_name: "fake adapter".to_string(),
}
}
#[test]
fn full_caps_run_the_engine() {
assert_eq!(engine_support(&caps(wgpu::DownlevelFlags::all())), Ok(()));
}
#[test]
fn a_downlevel_adapter_still_runs_the_engine() {
for flags in [
wgpu::DownlevelFlags::empty(),
wgpu::DownlevelFlags::all() - wgpu::DownlevelFlags::COMPUTE_SHADERS,
wgpu::DownlevelFlags::all() - wgpu::DownlevelFlags::INDIRECT_EXECUTION,
] {
assert_eq!(
engine_support(&caps(flags)),
Ok(()),
"the engine was refused on {flags:?}, which requires no flag at all"
);
}
}
#[test]
fn engine_requires_no_downlevel_flags() {
let none = wgpu::DownlevelFlags::empty();
assert!(none.contains(ENGINE_REQUIRED_DOWNLEVEL_FLAGS));
}
#[test]
fn a_missing_required_flag_is_refused_by_name() {
let required = wgpu::DownlevelFlags::COMPUTE_SHADERS;
let adapter = caps(wgpu::DownlevelFlags::all() - required);
let refusal = engine_support_with(required, &adapter)
.expect_err("an adapter missing a required flag must be refused");
assert_eq!(refusal.missing_flags, required);
let diagnosis = refusal.to_string();
assert!(diagnosis.contains("fake adapter"), "{diagnosis}");
assert!(diagnosis.contains("COMPUTE_SHADERS"), "{diagnosis}");
assert!(diagnosis.contains("frust-engine"), "{diagnosis}");
}
}