Revision control

Copy as Markdown

Other Tools

use {↩
core::fmt::{self, Debug},↩
gpu_alloc_types::{MemoryPropertyFlags, MemoryType},↩
};↩
bitflags::bitflags! {↩
/// Memory usage type.↩
/// Bits set define intended usage for requested memory.↩
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]↩
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]↩
pub struct UsageFlags: u8 {↩
/// Hints for allocator to find memory with faster device access.↩
/// If no flags is specified than `FAST_DEVICE_ACCESS` is implied.↩
const FAST_DEVICE_ACCESS = 0x01;↩
/// Memory will be accessed from host.↩
/// This flags guarantees that host memory operations will be available.↩
/// Otherwise implementation is encouraged to use non-host-accessible memory.↩
const HOST_ACCESS = 0x02;↩
/// Hints allocator that memory will be used for data downloading.↩
/// Allocator will strongly prefer host-cached memory.↩
/// Implies `HOST_ACCESS` flag.↩
const DOWNLOAD = 0x04;↩
/// Hints allocator that memory will be used for data uploading.↩
/// If `DOWNLOAD` flag is not set then allocator will assume that↩
/// host will access memory in write-only manner and may↩
/// pick not host-cached.↩
/// Implies `HOST_ACCESS` flag.↩
const UPLOAD = 0x08;↩
/// Hints allocator that memory will be used for short duration↩
/// allowing to use faster algorithm with less memory overhead.↩
/// If use holds returned memory block for too long then↩
/// effective memory overhead increases instead.↩
/// Best use case is for staging buffer for single batch of operations.↩
const TRANSIENT = 0x10;↩
/// Requests memory that can be addressed with `u64`.↩
/// Allows fetching device address for resources bound to that memory.↩
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↩
}↩
/// Returns mask with bits set for memory type indices that support the↩
/// usage.↩
pub fn mask(&self, usage: UsageFlags) -> u32 {↩
self.usages[usage.bits() as usize].mask↩
}↩
/// Returns slice of memory type indices that support the usage.↩
/// Earlier memory type has priority over later.↩
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| reverse_priority(usage, memory_types[index as usize].props));↩
let mask = types[..types_count as usize]↩
.iter()↩
.fold(0u32, |mask, index| mask | 1u32 << index);↩
MemoryForOneUsage {↩
mask,↩
types,↩
types_count,↩
}↩
}↩
fn compatible(usage: UsageFlags, flags: MemoryPropertyFlags) -> bool {↩
type Flags = MemoryPropertyFlags;↩
if flags.contains(Flags::LAZILY_ALLOCATED) || flags.contains(Flags::PROTECTED) {↩
// Unsupported↩
false
} else if usage.intersects(UsageFlags::HOST_ACCESS | UsageFlags::UPLOAD | UsageFlags::DOWNLOAD)↩
{↩
// Requires HOST_VISIBLE↩
flags.contains(Flags::HOST_VISIBLE)↩
} else {↩
true
}↩
}↩
/// Returns reversed priority of memory with specified flags for specified usage.↩
/// Lesser value returned = more prioritized.↩
fn reverse_priority(usage: UsageFlags, flags: MemoryPropertyFlags) -> u32 {↩
type Flags = MemoryPropertyFlags;↩
// Highly prefer device local memory when `FAST_DEVICE_ACCESS` usage is specified↩
// or usage is empty.↩
let device_local: bool = flags.contains(Flags::DEVICE_LOCAL)↩
^ (usage.is_empty() || usage.contains(UsageFlags::FAST_DEVICE_ACCESS));↩
assert!(↩
flags.contains(Flags::HOST_VISIBLE)↩
|| !usage↩
.intersects(UsageFlags::HOST_ACCESS | UsageFlags::UPLOAD | UsageFlags::DOWNLOAD)↩
);↩
// Prefer non-host-visible memory when host access is not required.↩
let host_visible: bool = flags.contains(Flags::HOST_VISIBLE)↩
^ usage.intersects(UsageFlags::HOST_ACCESS | UsageFlags::UPLOAD | UsageFlags::DOWNLOAD);↩
// Prefer cached memory for downloads.↩
// Or non-cached if downloads are not expected.↩
let host_cached: bool =↩
flags.contains(Flags::HOST_CACHED) ^ usage.contains(UsageFlags::DOWNLOAD);↩
// Prefer coherent for both uploads and downloads.↩
// Prefer non-coherent if neither flags is set.↩
let host_coherent: bool = flags.contains(Flags::HOST_COHERENT)↩
^ (usage.intersects(UsageFlags::UPLOAD | UsageFlags::DOWNLOAD));↩
// Each boolean is false if flags are preferred.↩
device_local as u32 * 8↩
+ host_visible as u32 * 4↩
+ host_cached as u32 * 2↩
+ host_coherent as u32↩
}↩