Skip to main content
← PSoXide SDK guides and examples · SDK f2fec1c301f1 · All Cargo features enabled; check feature gates before use.

psx_residency/
config.rs

1//! Budgets and policy knobs.
2
3use crate::backoff::Backoff;
4use crate::key::Class;
5use crate::pool::Placement;
6
7/// One page pool: its size and how new runs are placed.
8#[derive(Copy, Clone, Debug, PartialEq, Eq)]
9pub struct PoolConfig {
10    /// Pages in the pool (0 disables it).
11    pub capacity_pages: u32,
12    /// Gap choice for new runs.
13    pub placement: Placement,
14}
15
16impl PoolConfig {
17    /// A pool that holds nothing.
18    pub const UNUSED: PoolConfig = PoolConfig {
19        capacity_pages: 0,
20        placement: Placement::FirstFit,
21    };
22}
23
24/// Budget and behaviour of one category.
25#[derive(Copy, Clone, Debug, PartialEq, Eq)]
26pub struct CategoryConfig {
27    /// The pool (see [`Config::pools`]) this category allocates from. Several
28    /// categories may share one.
29    pub pool: u8,
30    /// Most pages this category may hold, across all its entries.
31    pub max_pages: u32,
32    /// Most entries (resident or in flight) this category may hold. 0 disables
33    /// the category.
34    pub max_entries: u16,
35    /// Whether compaction may move this category's unpinned entries. A
36    /// category whose bytes are addressed by hardware or by long-lived raw
37    /// pointers (VRAM, SPU RAM, archetype packs) is not movable.
38    pub movable: bool,
39    /// Whether a landed payload needs an install step (verify, relocate,
40    /// upload) before it is resident. When false a finished read is resident.
41    pub installs: bool,
42    /// Unwanted entries farther than this are free victims; nearer ones are
43    /// in the hysteresis band and go last. Set it to the lead radius plus the
44    /// hysteresis, see [`CategoryConfig::keep_radius_for`].
45    pub keep_radius: u32,
46}
47
48impl CategoryConfig {
49    /// A disabled category.
50    pub const UNUSED: CategoryConfig = CategoryConfig {
51        pool: 0,
52        max_pages: 0,
53        max_entries: 0,
54        movable: false,
55        installs: false,
56        keep_radius: 0,
57    };
58
59    /// The eviction keep radius the design prescribes: the lead radius plus
60    /// a hysteresis of `max(region_width, lead_radius / 4)`, so a player
61    /// standing in a doorway cannot make two regions thrash.
62    pub const fn keep_radius_for(lead_radius: u32, region_width: u32) -> u32 {
63        let quarter = lead_radius / 4;
64        let hysteresis = if region_width > quarter {
65            region_width
66        } else {
67            quarter
68        };
69        lead_radius.saturating_add(hysteresis)
70    }
71}
72
73/// Everything configurable about a [`crate::Residency`]. `CATEGORIES` is both
74/// the number of category numbers in use and the number of pools.
75#[derive(Copy, Clone, Debug, PartialEq, Eq)]
76pub struct Config<const CATEGORIES: usize> {
77    /// Page pools, indexed by pool number.
78    pub pools: [PoolConfig; CATEGORIES],
79    /// Category budgets, indexed by [`crate::Category::number`]. A key whose
80    /// category number is out of range is ignored (and counted).
81    pub categories: [CategoryConfig; CATEGORIES],
82    /// Requests the transport may hold at once (the design's queue depth
83    /// is 2). Cancelling requests do not count.
84    pub max_in_flight: u8,
85    /// Bookkeeping budget per [`crate::Residency::step`], in work units (see
86    /// the crate docs). `u32::MAX` means unlimited.
87    pub step_budget: u32,
88    /// Retry schedule for failed reads and installs.
89    pub backoff: Backoff,
90    /// A wanted entry of the same class as a requester is displaced only when
91    /// it is farther than the requester by more than this (distance units),
92    /// so two entries of nearly equal distance cannot trade places each frame.
93    pub displace_margin: u32,
94    /// Per requester class, the highest victim tier it may evict: 0 is an
95    /// unwanted entry beyond the keep radius, 1 an unwanted entry inside the
96    /// hysteresis band, 2 a wanted entry of lower priority than the
97    /// requester. Indexed by [`Class::as_index`].
98    pub max_victim_tier: [u8; Class::COUNT],
99    /// Per class, whether an entry wanted at that class this frame can never
100    /// be evicted this frame. Indexed by [`Class::as_index`].
101    pub protect_wanted: [bool; Class::COUNT],
102    /// Let a [`Class::Demand`] miss cancel the least urgent in-flight request
103    /// when the transport is at depth.
104    pub cancel_for_demand: bool,
105}
106
107impl<const CATEGORIES: usize> Config<CATEGORIES> {
108    /// All pools and categories disabled, the design's default knobs: queue
109    /// depth 2, unlimited step budget, the grid backoff, tiers
110    /// `[2, 2, 1, 0, 0]`, demand and combat fill protected, cancel for demand.
111    pub const fn new() -> Self {
112        Self {
113            pools: [PoolConfig::UNUSED; CATEGORIES],
114            categories: [CategoryConfig::UNUSED; CATEGORIES],
115            max_in_flight: 2,
116            step_budget: u32::MAX,
117            backoff: Backoff::GRID_DEFAULT,
118            displace_margin: 0,
119            max_victim_tier: [2, 2, 1, 0, 0],
120            protect_wanted: [true, true, false, false, false],
121            cancel_for_demand: true,
122        }
123    }
124}
125
126impl<const CATEGORIES: usize> Default for Config<CATEGORIES> {
127    fn default() -> Self {
128        Self::new()
129    }
130}