rendy_factory/config.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
use std::cmp::min;
use crate::{
command::FamilyId,
core::DeviceId,
memory::{DynamicConfig, HeapsConfig, LinearConfig},
};
/// Factory initialization config.
///
/// `devices` - [`DeviceConfigure`] implementation instance to pick physical device.
/// [`BasicDevicesConfigure`] can be used as sane default.
/// `heaps` - [`HeapsConfigure`] implementation instance to cofigure memory allocators.
/// [`BasicHeapsConfigure`] can be used as sane default.
/// `queues` - [`QueuesConfigure`] implementation to configure device queues creation.
/// [`OneGraphicsQueue`] can be used if only one graphics queue will satisfy requirements.
///
/// [`DeviceConfigure`]: trait.DevicesConfigure.html
/// [`BasicDevicesConfigure`]: struct.BasicDevicesConfigure.html
/// [`HeapsConfigure`]: trait.HeapsConfigure.html
/// [`BasicHeapsConfigure`]: struct.BasicHeapsConfigure.html
/// [`QueuesConfigure`]: trait.QueuesConfigure.html
/// [`OneGraphicsQueue`]: struct.OneGraphicsQueue.html
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Config<D = BasicDevicesConfigure, H = BasicHeapsConfigure, Q = OneGraphicsQueue> {
/// Config to choose adapter.
pub devices: D,
/// Config for memory::Heaps.
pub heaps: H,
/// Config for queue families.
pub queues: Q,
}
/// Queues configuration.
///
/// Method [`configure`] receives collection of queue families and
/// returns an iterator over family ids and number of queues.
///
/// [`configure`]: trait.QueuesConfigure.html#tymethod.configure
pub unsafe trait QueuesConfigure {
/// Slice of priorities.
type Priorities: AsRef<[f32]>;
/// Iterator over families to create.
type Families: IntoIterator<Item = (FamilyId, Self::Priorities)>;
/// Configure.
fn configure(
&self,
device: DeviceId,
families: &[impl rendy_core::hal::queue::QueueFamily],
) -> Self::Families;
}
/// QueuePicker that picks first graphics queue family.
///
/// TODO: Try to pick family that is capable of presenting
/// This is possible in platform-dependent way for some platforms.
///
/// To pick multiple families with require number of queues
/// a custom [`QueuesConfigure`] implementation can be used instead.
///
/// [`QueuesConfigure`]: trait.QueuesConfigure.html
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OneGraphicsQueue;
unsafe impl QueuesConfigure for OneGraphicsQueue {
type Priorities = [f32; 1];
type Families = Option<(FamilyId, [f32; 1])>;
fn configure(
&self,
device: DeviceId,
families: &[impl rendy_core::hal::queue::QueueFamily],
) -> Option<(FamilyId, [f32; 1])> {
families
.iter()
.find(|f| f.queue_type().supports_graphics() && f.max_queues() > 0)
.map(|f| {
(
FamilyId {
device,
index: f.id().0,
},
[1.0],
)
})
}
}
/// Saved config for queues.
/// This config can be loaded from config files
/// in any format supported by serde ecosystem.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SavedQueueConfig(Vec<(usize, Vec<f32>)>);
unsafe impl QueuesConfigure for SavedQueueConfig {
type Priorities = Vec<f32>;
type Families = Vec<(FamilyId, Vec<f32>)>;
fn configure(
&self,
device: DeviceId,
_: &[impl rendy_core::hal::queue::QueueFamily],
) -> Vec<(FamilyId, Vec<f32>)> {
// TODO: FamilyId should be stored directly once it become serializable.
self.0
.iter()
.map(|(id, vec)| (FamilyId { device, index: *id }, vec.clone()))
.collect()
}
}
/// Heaps configuration.
///
/// Method [`configure`] receives memory properties and
/// emits iterator memory types together with configurations for allocators and
/// iterator over heaps sizes.
///
/// [`configure`]: trait.HeapsConfigure.html#tymethod.configure
pub unsafe trait HeapsConfigure {
/// Iterator over memory types.
type Types: IntoIterator<Item = (rendy_core::hal::memory::Properties, u32, HeapsConfig)>;
/// Iterator over heaps.
type Heaps: IntoIterator<Item = u64>;
/// Configure.
fn configure(
&self,
properties: &rendy_core::hal::adapter::MemoryProperties,
) -> (Self::Types, Self::Heaps);
}
/// Basic heaps config.
/// It uses some arbitrary values that can be considered sane default
/// for today (year 2019) hardware and software.
///
/// If default allocators configuration is suboptimal for the particular use case
/// a custom [`HeapsConfigure`] implementation can be used instead.
///
/// [`HeapsConfigure`]: trait.HeapsConfigure.html
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BasicHeapsConfigure;
unsafe impl HeapsConfigure for BasicHeapsConfigure {
type Types = Vec<(rendy_core::hal::memory::Properties, u32, HeapsConfig)>;
type Heaps = Vec<u64>;
fn configure(
&self,
properties: &rendy_core::hal::adapter::MemoryProperties,
) -> (Self::Types, Self::Heaps) {
let _1mb = 1024 * 1024;
let _32mb = 32 * _1mb;
let _128mb = 128 * _1mb;
let types = properties
.memory_types
.iter()
.map(|mt| {
let config = HeapsConfig {
linear: if mt
.properties
.contains(rendy_core::hal::memory::Properties::CPU_VISIBLE)
{
Some(LinearConfig {
linear_size: min(_128mb, properties.memory_heaps[mt.heap_index] / 16),
})
} else {
None
},
dynamic: Some(DynamicConfig {
block_size_granularity: 256.min(
(properties.memory_heaps[mt.heap_index] / 4096).next_power_of_two(),
),
min_device_allocation: _1mb
.min(properties.memory_heaps[mt.heap_index] / 1048)
.next_power_of_two(),
max_chunk_size: _32mb.min(
(properties.memory_heaps[mt.heap_index] / 128).next_power_of_two(),
),
}),
};
(mt.properties, mt.heap_index as u32, config)
})
.collect();
let heaps = properties.memory_heaps.iter().cloned().collect();
(types, heaps)
}
}
/// Saved config for allocators.
/// This config can be loaded from config files
/// in any format supported by serde ecosystem.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SavedHeapsConfig {
types: Vec<(rendy_core::hal::memory::Properties, u32, HeapsConfig)>,
heaps: Vec<u64>,
}
unsafe impl HeapsConfigure for SavedHeapsConfig {
type Types = Vec<(rendy_core::hal::memory::Properties, u32, HeapsConfig)>;
type Heaps = Vec<u64>;
fn configure(
&self,
_properties: &rendy_core::hal::adapter::MemoryProperties,
) -> (Self::Types, Self::Heaps) {
(self.types.clone(), self.heaps.clone())
}
}
/// Devices configuration.
/// Picks physical device to use.
pub trait DevicesConfigure {
/// Pick adapter from the slice.
///
/// # Panics
///
/// This function may panic if empty slice is provided.
///
fn pick<B>(&self, adapters: &[rendy_core::hal::adapter::Adapter<B>]) -> usize
where
B: rendy_core::hal::Backend;
}
/// Basics adapters config.
///
/// It picks first device with highest priority.
/// From highest - discrete GPU, to lowest - CPU.
///
/// To pick among presented discret GPUs,
/// or to intentionally pick integrated GPU when discrete GPU is available
/// a custom [`DeviceConfigure`] implementationcan be used instead.
///
/// [`DeviceConfigure`]: trait.DevicesConfigure.html
#[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BasicDevicesConfigure;
impl DevicesConfigure for BasicDevicesConfigure {
fn pick<B>(&self, adapters: &[rendy_core::hal::adapter::Adapter<B>]) -> usize
where
B: rendy_core::hal::Backend,
{
adapters
.iter()
.enumerate()
.min_by_key(|(_, adapter)| match adapter.info.device_type {
rendy_core::hal::adapter::DeviceType::DiscreteGpu => 0,
rendy_core::hal::adapter::DeviceType::IntegratedGpu => 1,
rendy_core::hal::adapter::DeviceType::VirtualGpu => 2,
rendy_core::hal::adapter::DeviceType::Cpu => 3,
_ => 4,
})
.expect("No adapters present")
.0
}
}