#[derive(Clone, Debug)]
pub struct OffscreenBudget {
pub max_targets: usize,
pub max_total_pixels: u64,
pub current_pixels: u64,
pub current_targets: usize,
}
impl Default for OffscreenBudget {
fn default() -> Self {
Self {
max_targets: 8,
max_total_pixels: 1920u64 * 1080 * 4,
current_pixels: 0,
current_targets: 0,
}
}
}
impl OffscreenBudget {
pub fn mobile() -> Self {
Self {
max_targets: 4,
max_total_pixels: 1280u64 * 720 * 2,
current_pixels: 0,
current_targets: 0,
}
}
pub fn can_allocate(&self, width: u32, height: u32) -> bool {
let pixels = width as u64 * height as u64;
self.current_targets < self.max_targets
&& self.current_pixels + pixels <= self.max_total_pixels
}
pub fn register(&mut self, width: u32, height: u32) {
self.current_pixels += width as u64 * height as u64;
self.current_targets += 1;
}
pub fn release(&mut self, width: u32, height: u32) {
self.current_pixels = self
.current_pixels
.saturating_sub(width as u64 * height as u64);
self.current_targets = self.current_targets.saturating_sub(1);
}
pub fn reset(&mut self) {
self.current_pixels = 0;
self.current_targets = 0;
}
pub fn is_exhausted(&self) -> bool {
self.current_targets >= self.max_targets
}
}
#[cfg(test)]
mod p1_27_offscreen_budget_tests {
use super::OffscreenBudget;
#[test]
fn default_budget_allows_allocation() {
let budget = OffscreenBudget::default();
assert!(budget.can_allocate(1920, 1080));
}
#[test]
fn mobile_budget_has_lower_limits() {
let budget = OffscreenBudget::mobile();
assert!(budget.can_allocate(1280, 720));
assert!(!budget.can_allocate(3840, 2160)); }
#[test]
fn budget_tracks_registration() {
let mut budget = OffscreenBudget::default();
budget.register(1920, 1080);
assert_eq!(budget.current_targets, 1);
assert_eq!(budget.current_pixels, 1920u64 * 1080);
}
#[test]
fn budget_enforces_max_targets() {
let mut budget = OffscreenBudget {
max_targets: 2,
max_total_pixels: u64::MAX,
current_pixels: 0,
current_targets: 0,
};
budget.register(100, 100);
budget.register(100, 100);
assert!(!budget.can_allocate(100, 100)); assert!(budget.is_exhausted());
}
#[test]
fn budget_enforces_pixel_limit() {
let mut budget = OffscreenBudget {
max_targets: 100,
max_total_pixels: 1000,
current_pixels: 0,
current_targets: 0,
};
assert!(budget.can_allocate(10, 10)); budget.register(10, 10);
assert!(!budget.can_allocate(100, 10)); }
#[test]
fn release_frees_budget() {
let mut budget = OffscreenBudget::default();
budget.register(1920, 1080);
budget.release(1920, 1080);
assert_eq!(budget.current_targets, 0);
assert_eq!(budget.current_pixels, 0);
}
#[test]
fn reset_clears_all() {
let mut budget = OffscreenBudget::default();
budget.register(1920, 1080);
budget.register(1280, 720);
budget.reset();
assert_eq!(budget.current_targets, 0);
assert_eq!(budget.current_pixels, 0);
}
}