use super::hdr_asset;
use crate::settings::LiveComputeTarget;
use indicatrix::renderer::env_map::EnvironmentMap;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RemoteRefusal {
HdrEnvironment,
HdrNotTransferable,
}
impl RemoteRefusal {
#[must_use]
pub const fn live_note(self) -> &'static str {
match self {
Self::HdrEnvironment => {
"This remote doesn't support HDR environments; rendering locally"
}
Self::HdrNotTransferable => {
"This HDR environment can't be sent to the remote; rendering locally"
}
}
}
#[must_use]
pub const fn export_note(self) -> &'static str {
match self {
Self::HdrEnvironment => {
"This export uses an HDR environment map, which the remote does not \
support -- rendering locally only so the whole image is lit by the \
same environment."
}
Self::HdrNotTransferable => {
"This export uses an HDR environment map that cannot be sent to the \
remote -- rendering locally only so the whole image is lit by the same \
environment."
}
}
}
}
pub fn remote_can_render(
env_map: Option<&Arc<EnvironmentMap>>,
remote_hdr: bool,
) -> Result<(), RemoteRefusal> {
let Some(map) = env_map else {
return Ok(());
};
if !hdr_asset::asset_for(map).is_some_and(|asset| asset.transferable()) {
return Err(RemoteRefusal::HdrNotTransferable);
}
if remote_hdr {
Ok(())
} else {
Err(RemoteRefusal::HdrEnvironment)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LiveDispatch {
Remote,
Local,
Refused(RemoteRefusal),
}
#[must_use]
pub fn live_remote_dispatch(
target: LiveComputeTarget,
worker_configured: bool,
env_map: Option<&Arc<EnvironmentMap>>,
remote_hdr: Option<bool>,
) -> LiveDispatch {
if matches!(target, LiveComputeTarget::LocalOnly) || !worker_configured {
return LiveDispatch::Local;
}
match remote_can_render(env_map, remote_hdr.unwrap_or(true)) {
Ok(()) => LiveDispatch::Remote,
Err(refusal) => LiveDispatch::Refused(refusal),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn loaded_hdr() -> Arc<EnvironmentMap> {
let pixels = vec![image::Rgb([0.5f32, 0.5, 0.5]); 32];
let mut bytes = Vec::new();
image::codecs::hdr::HdrEncoder::new(&mut bytes)
.encode(&pixels, 8, 4)
.unwrap();
let path = std::env::temp_dir().join(format!(
"indicatrix-cut-guard-{}-{:?}.hdr",
std::process::id(),
std::thread::current().id()
));
std::fs::write(&path, &bytes).unwrap();
let (map, _asset) = hdr_asset::load_hdr_file(&path).unwrap();
let _ = std::fs::remove_file(&path);
map
}
fn synthetic_hdr() -> Arc<EnvironmentMap> {
Arc::new(EnvironmentMap::uniform(4, 4, [0.5, 0.5, 0.5]))
}
#[test]
fn an_hdr_scene_may_render_remotely_exactly_when_the_remote_supports_hdr() {
let map = loaded_hdr();
assert_eq!(remote_can_render(None, false), Ok(()));
assert_eq!(remote_can_render(None, true), Ok(()));
assert_eq!(remote_can_render(Some(&map), true), Ok(()));
assert_eq!(
remote_can_render(Some(&map), false),
Err(RemoteRefusal::HdrEnvironment)
);
for remote_hdr in [false, true] {
assert_eq!(
remote_can_render(Some(&synthetic_hdr()), remote_hdr),
Err(RemoteRefusal::HdrNotTransferable)
);
}
}
#[test]
fn the_live_view_dispatches_an_hdr_scene_only_to_an_hdr_capable_remote() {
let map = loaded_hdr();
for target in [LiveComputeTarget::RemoteOnly, LiveComputeTarget::Both] {
assert_eq!(
live_remote_dispatch(target, true, Some(&map), Some(false)),
LiveDispatch::Refused(RemoteRefusal::HdrEnvironment),
"{target:?}, remote without HDR"
);
assert_eq!(
live_remote_dispatch(target, true, Some(&map), Some(true)),
LiveDispatch::Remote,
"{target:?}, HDR-capable remote"
);
assert_eq!(
live_remote_dispatch(target, true, Some(&map), None),
LiveDispatch::Remote
);
assert_eq!(
live_remote_dispatch(target, true, Some(&synthetic_hdr()), Some(true)),
LiveDispatch::Refused(RemoteRefusal::HdrNotTransferable)
);
}
assert_eq!(
live_remote_dispatch(LiveComputeTarget::LocalOnly, true, Some(&map), Some(true)),
LiveDispatch::Local
);
}
#[test]
fn a_studio_scene_dispatches_only_with_a_worker_and_a_remote_mode() {
for remote_hdr in [None, Some(false), Some(true)] {
assert_eq!(
live_remote_dispatch(LiveComputeTarget::Both, true, None, remote_hdr),
LiveDispatch::Remote
);
assert_eq!(
live_remote_dispatch(LiveComputeTarget::RemoteOnly, true, None, remote_hdr),
LiveDispatch::Remote
);
assert_eq!(
live_remote_dispatch(LiveComputeTarget::LocalOnly, true, None, remote_hdr),
LiveDispatch::Local
);
assert_eq!(
live_remote_dispatch(LiveComputeTarget::Both, false, None, remote_hdr),
LiveDispatch::Local
);
}
}
#[test]
fn the_live_note_is_the_agreed_wording() {
assert_eq!(
RemoteRefusal::HdrEnvironment.live_note(),
"This remote doesn't support HDR environments; rendering locally"
);
assert!(RemoteRefusal::HdrEnvironment.export_note().contains("HDR"));
assert!(
RemoteRefusal::HdrNotTransferable
.export_note()
.contains("HDR")
);
}
}