use {
core::fmt::{self, Debug},
gpu_alloc_types::{MemoryPropertyFlags, MemoryType},
};
bitflags::bitflags! {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UsageFlags: u8 {
const FAST_DEVICE_ACCESS = 0x01;
const HOST_ACCESS = 0x02;
const DOWNLOAD = 0x04;
const UPLOAD = 0x08;
const TRANSIENT = 0x10;
const DEVICE_ADDRESS = 0x20;
}
}
#[derive(Clone, Copy, Debug)]
struct MemoryForOneUsage {
mask: u32,
types: [u32; 32],
types_count: u32,
}
pub(crate) struct MemoryForUsage {
usages: [MemoryForOneUsage; 64],
}
impl Debug for MemoryForUsage {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("MemoryForUsage")
.field("usages", &&self.usages[..])
.finish()
}
}
impl MemoryForUsage {
pub fn new(memory_types: &[MemoryType]) -> Self {
assert!(
memory_types.len() <= 32,
"Only up to 32 memory types supported"
);
let mut mfu = MemoryForUsage {
usages: [MemoryForOneUsage {
mask: 0,
types: [0; 32],
types_count: 0,
}; 64],
};
for usage in 0..64 {
mfu.usages[usage as usize] =
one_usage(UsageFlags::from_bits_truncate(usage), memory_types);
}
mfu
}
pub fn mask(&self, usage: UsageFlags) -> u32 {
self.usages[usage.bits() as usize].mask
}
pub fn types(&self, usage: UsageFlags) -> &[u32] {
let usage = &self.usages[usage.bits() as usize];
&usage.types[..usage.types_count as usize]
}
}
fn one_usage(usage: UsageFlags, memory_types: &[MemoryType]) -> MemoryForOneUsage {
let mut types = [0; 32];
let mut types_count = 0;
for (index, mt) in memory_types.iter().enumerate() {
if compatible(usage, mt.props) {
types[types_count as usize] = index as u32;
types_count += 1;
}
}
types[..types_count as usize]
.sort_unstable_by_key(|&index| priority(usage, memory_types[index as usize].props));
let mask = types[..types_count as usize]
.iter()
.fold(0u32, |mask, index| mask | 1u32 << index);
MemoryForOneUsage {
types,
mask,
types_count,
}
}
fn compatible(usage: UsageFlags, flags: MemoryPropertyFlags) -> bool {
type Flags = MemoryPropertyFlags;
if flags.contains(Flags::LAZILY_ALLOCATED) || flags.contains(Flags::PROTECTED) {
false
} else if usage.intersects(UsageFlags::HOST_ACCESS | UsageFlags::UPLOAD | UsageFlags::DOWNLOAD)
{
flags.contains(Flags::HOST_VISIBLE)
} else {
true
}
}
fn priority(usage: UsageFlags, flags: MemoryPropertyFlags) -> u32 {
type Flags = MemoryPropertyFlags;
let device_local: bool = flags.contains(Flags::DEVICE_LOCAL)
^ (usage.is_empty() || usage.contains(UsageFlags::FAST_DEVICE_ACCESS));
let host_visible: bool = flags.contains(Flags::HOST_VISIBLE)
&& !usage.intersects(UsageFlags::HOST_ACCESS | UsageFlags::UPLOAD | UsageFlags::DOWNLOAD);
let cached: bool = flags.contains(Flags::HOST_CACHED) ^ usage.contains(UsageFlags::DOWNLOAD);
let coherent: bool = flags.contains(Flags::HOST_COHERENT)
^ (usage.intersects(UsageFlags::UPLOAD | UsageFlags::DOWNLOAD));
device_local as u32 * 8 + host_visible as u32 * 4 + cached as u32 * 2 + coherent as u32
}