#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BindlessSupport {
Full,
Static,
Unsupported,
}
impl std::fmt::Display for BindlessSupport {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Full => f.write_str("full"),
Self::Static => f.write_str("static"),
Self::Unsupported => f.write_str("unsupported"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BindlessInputs {
pub resource_count: u32,
pub support: BindlessSupport,
pub dynamic_indexing: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BindlessDecision {
Bindless,
TraditionalBindings,
}
impl std::fmt::Display for BindlessDecision {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Bindless => f.write_str("bindless"),
Self::TraditionalBindings => f.write_str("traditional-bindings"),
}
}
}
pub const BINDLESS_RESOURCE_COUNT_THRESHOLD: u32 = 24;
#[must_use]
pub fn decide_bindless(inputs: BindlessInputs) -> BindlessDecision {
if matches!(inputs.support, BindlessSupport::Unsupported) {
return BindlessDecision::TraditionalBindings;
}
if inputs.resource_count < BINDLESS_RESOURCE_COUNT_THRESHOLD {
return BindlessDecision::TraditionalBindings;
}
match inputs.support {
BindlessSupport::Full => BindlessDecision::Bindless,
BindlessSupport::Static if !inputs.dynamic_indexing => BindlessDecision::Bindless,
_ => BindlessDecision::TraditionalBindings,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn inp(count: u32, support: BindlessSupport, dynamic: bool) -> BindlessInputs {
BindlessInputs {
resource_count: count,
support,
dynamic_indexing: dynamic,
}
}
#[test]
fn unsupported_always_returns_traditional() {
for count in [0, 8, 24, 100, u32::MAX] {
for dynamic in [false, true] {
assert_eq!(
decide_bindless(inp(count, BindlessSupport::Unsupported, dynamic)),
BindlessDecision::TraditionalBindings
);
}
}
}
#[test]
fn below_threshold_returns_traditional_on_full_support() {
assert_eq!(
decide_bindless(inp(23, BindlessSupport::Full, true)),
BindlessDecision::TraditionalBindings
);
}
#[test]
fn at_threshold_returns_bindless_on_full_support() {
assert_eq!(
decide_bindless(inp(24, BindlessSupport::Full, true)),
BindlessDecision::Bindless
);
}
#[test]
fn above_threshold_returns_bindless_on_full_support() {
assert_eq!(
decide_bindless(inp(100, BindlessSupport::Full, false)),
BindlessDecision::Bindless
);
}
#[test]
fn static_support_with_dynamic_access_returns_traditional() {
assert_eq!(
decide_bindless(inp(100, BindlessSupport::Static, true)),
BindlessDecision::TraditionalBindings
);
}
#[test]
fn static_support_with_static_access_returns_bindless() {
assert_eq!(
decide_bindless(inp(100, BindlessSupport::Static, false)),
BindlessDecision::Bindless
);
}
#[test]
fn static_support_below_threshold_returns_traditional() {
assert_eq!(
decide_bindless(inp(10, BindlessSupport::Static, false)),
BindlessDecision::TraditionalBindings
);
}
#[test]
fn zero_resources_always_traditional() {
for support in [
BindlessSupport::Full,
BindlessSupport::Static,
BindlessSupport::Unsupported,
] {
assert_eq!(
decide_bindless(inp(0, support, false)),
BindlessDecision::TraditionalBindings
);
}
}
#[test]
fn threshold_constant_matches_documentation() {
assert_eq!(BINDLESS_RESOURCE_COUNT_THRESHOLD, 24);
}
#[test]
fn bindless_support_formats_debug_stable() {
assert_eq!(format!("{}", BindlessSupport::Full), "full");
assert_eq!(format!("{}", BindlessSupport::Static), "static");
assert_eq!(format!("{}", BindlessSupport::Unsupported), "unsupported");
}
#[test]
fn bindless_decision_formats_human_readable_string() {
assert_eq!(
format!("{}", BindlessDecision::Bindless),
"bindless"
);
assert_eq!(
format!("{}", BindlessDecision::TraditionalBindings),
"traditional-bindings"
);
}
}