use crate::settings::{ExportTransfer, WorkerSettings};
pub const MIN_EXPORT_DIM: u32 = 16;
pub const MAX_EXPORT_DIM: u32 = 8192;
pub const MIN_EXPORT_SPP: u32 = 1;
pub const MAX_EXPORT_SPP: u32 = 32768;
pub const MIN_EXPORT_BOUNCES: u32 = 1;
pub const MAX_EXPORT_BOUNCES: u32 = 128;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExportParams {
pub width: u32,
pub height: u32,
pub samples_per_pixel: u32,
pub max_bounces: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComputeTarget {
LocalOnly,
RemoteOnly,
Both,
}
impl ComputeTarget {
#[must_use]
pub const fn pauses_live_viewport(self) -> bool {
!matches!(self, Self::RemoteOnly)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RemoteSelection {
pub compute_target: ComputeTarget,
pub worker: Option<WorkerSettings>,
pub transfer: ExportTransfer,
pub contribute_local: bool,
}
#[cfg(test)]
impl RemoteSelection {
#[must_use]
pub const fn local_only() -> Self {
Self {
compute_target: ComputeTarget::LocalOnly,
worker: None,
transfer: ExportTransfer::FullData,
contribute_local: false,
}
}
}
pub fn validate_export_params(
width: i32,
height: i32,
samples_per_pixel: i32,
max_bounces: i32,
) -> Result<ExportParams, String> {
if width <= 0 || height <= 0 || samples_per_pixel <= 0 || max_bounces <= 0 {
return Err(
"Width, height, sample count, and max bounces must all be positive.".to_string(),
);
}
let width = width as u32;
let height = height as u32;
let samples_per_pixel = samples_per_pixel as u32;
let max_bounces = max_bounces as u32;
if width < MIN_EXPORT_DIM || height < MIN_EXPORT_DIM {
return Err(format!(
"Minimum export size is {MIN_EXPORT_DIM}x{MIN_EXPORT_DIM} px."
));
}
if width > MAX_EXPORT_DIM || height > MAX_EXPORT_DIM {
return Err(format!(
"Maximum export size is {MAX_EXPORT_DIM}x{MAX_EXPORT_DIM} px (requested {width}x{height})."
));
}
if samples_per_pixel < MIN_EXPORT_SPP {
return Err("Sample count must be at least 1.".to_string());
}
if samples_per_pixel > MAX_EXPORT_SPP {
return Err(format!(
"Maximum sample count is {MAX_EXPORT_SPP} samples per pixel."
));
}
if !(MIN_EXPORT_BOUNCES..=MAX_EXPORT_BOUNCES).contains(&max_bounces) {
return Err(format!(
"Max bounces must be between {MIN_EXPORT_BOUNCES} and {MAX_EXPORT_BOUNCES}."
));
}
Ok(ExportParams {
width,
height,
samples_per_pixel,
max_bounces,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validate_export_params_rejects_zero_and_negative() {
assert!(validate_export_params(0, 1080, 64, 12).is_err());
assert!(validate_export_params(1920, 0, 64, 12).is_err());
assert!(validate_export_params(1920, 1080, 0, 12).is_err());
assert!(validate_export_params(1920, 1080, 64, 0).is_err());
assert!(validate_export_params(-1920, 1080, 64, 12).is_err());
assert!(validate_export_params(1920, -1080, 64, 12).is_err());
assert!(validate_export_params(1920, 1080, -1, 12).is_err());
assert!(validate_export_params(1920, 1080, 64, -1).is_err());
}
#[test]
fn validate_export_params_rejects_absurdly_large_requests() {
let err = validate_export_params(100_000, 100_000, 64, 12).unwrap_err();
assert!(err.contains("Maximum export size"));
}
#[test]
fn validate_export_params_rejects_excessive_sample_counts() {
let err = validate_export_params(1920, 1080, 1_000_000, 12).unwrap_err();
assert!(err.contains("Maximum sample count"));
}
#[test]
fn validate_export_params_rejects_tiny_dimensions_below_minimum() {
assert!(validate_export_params(1, 1, 64, 12).is_err());
}
#[test]
fn validate_export_params_rejects_bounce_caps_outside_the_ladders_range() {
let err = validate_export_params(1920, 1080, 64, 129).unwrap_err();
assert!(err.contains("Max bounces"));
assert!(validate_export_params(1920, 1080, 64, 0).is_err());
}
#[test]
fn validate_export_params_accepts_sensible_presets() {
assert_eq!(
validate_export_params(1920, 1080, 256, 12).unwrap(),
ExportParams {
width: 1920,
height: 1080,
samples_per_pixel: 256,
max_bounces: 12,
}
);
assert_eq!(
validate_export_params(3840, 2160, 64, 128).unwrap(),
ExportParams {
width: 3840,
height: 2160,
samples_per_pixel: 64,
max_bounces: 128,
}
);
}
#[test]
fn validate_export_params_accepts_boundary_values() {
assert!(
validate_export_params(
MIN_EXPORT_DIM as i32,
MIN_EXPORT_DIM as i32,
MIN_EXPORT_SPP as i32,
MIN_EXPORT_BOUNCES as i32,
)
.is_ok()
);
assert!(
validate_export_params(
MAX_EXPORT_DIM as i32,
MAX_EXPORT_DIM as i32,
MAX_EXPORT_SPP as i32,
MAX_EXPORT_BOUNCES as i32,
)
.is_ok()
);
}
}