use super::{
super::types::{
HandleKind, RemoteCommand, RemoteError, RemoteFinalImageRequest, RemoteRenderHandle,
RemoteRenderRequest, RemoteStream, RemoteUpdate,
},
handshake::connect_and_handshake,
stream_io::{to_remote_update, try_read_stream_event},
};
use crate::settings::WorkerSettings;
use indicatrix_net::{
client::{Accumulator, ApplyOutcome},
messages::{FinalImageRequest, FinalOutput, RenderCapability, RenderRequest, StreamEvent},
};
use std::{
sync::{
Arc, Mutex, PoisonError,
mpsc::{self, TryRecvError},
},
thread,
time::{Duration, Instant},
};
enum OneShotRequest {
Render(RemoteRenderRequest),
FinalImage(RemoteFinalImageRequest),
}
impl OneShotRequest {
const fn worker(&self) -> &WorkerSettings {
match self {
Self::Render(r) => &r.worker,
Self::FinalImage(r) => &r.worker,
}
}
const fn request_id(&self) -> u32 {
match self {
Self::Render(r) => r.request_id,
Self::FinalImage(r) => r.request_id,
}
}
const fn scene(&self) -> &indicatrix_net::SceneState {
match self {
Self::Render(r) => &r.scene,
Self::FinalImage(r) => &r.scene,
}
}
const fn range(&self) -> (u32, u32) {
match self {
Self::Render(r) => (r.first_sample, r.samples),
Self::FinalImage(r) => (r.first_sample, r.samples),
}
}
fn send(
&self,
stream: &mut RemoteStream,
capability: &RenderCapability,
) -> Result<(), RemoteError> {
match self {
Self::Render(request) => {
let render_request = RenderRequest {
request_id: request.request_id,
scene: request.scene.clone(),
first_sample: request.first_sample,
samples: request.samples,
stream: request
.worker
.export_stream_config(capability.min_cadence_ms),
intent: request.intent,
};
indicatrix_net::client::send_render_request(stream, &render_request)?;
}
Self::FinalImage(request) => {
let final_request = FinalImageRequest {
request_id: request.request_id,
scene: request.scene.clone(),
first_sample: request.first_sample,
samples: request.samples,
width: request.scene.width,
height: request.scene.height,
color_space: request.color_space.into(),
output: FinalOutput::PngRgba8,
viewer_samples: request.viewer_samples,
};
indicatrix_net::client::send_final_image_request(stream, &final_request)?;
}
}
Ok(())
}
}
pub fn spawn_remote_render(
request: RemoteRenderRequest,
accumulator: Arc<Mutex<Accumulator>>,
on_update: impl FnMut(RemoteUpdate) + Send + 'static,
) -> RemoteRenderHandle {
spawn(OneShotRequest::Render(request), accumulator, on_update)
}
pub fn spawn_final_image_request(
request: RemoteFinalImageRequest,
accumulator: Arc<Mutex<Accumulator>>,
on_update: impl FnMut(RemoteUpdate) + Send + 'static,
) -> RemoteRenderHandle {
spawn(OneShotRequest::FinalImage(request), accumulator, on_update)
}
fn spawn(
request: OneShotRequest,
accumulator: Arc<Mutex<Accumulator>>,
mut on_update: impl FnMut(RemoteUpdate) + Send + 'static,
) -> RemoteRenderHandle {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let request_id = request.request_id();
let result = run(
&request,
&accumulator,
&rx,
&mut on_update,
super::LIVENESS_TIMEOUT,
);
if let Err(e) = result {
on_update(RemoteUpdate::Failed {
request_id,
message: e.to_string(),
});
}
});
RemoteRenderHandle(HandleKind::OneShot(tx))
}
fn ensure_cancel_acknowledged(cancel_sent_at: Option<Instant>) -> Result<(), RemoteError> {
let now = Instant::now();
if super::cancel_ack_overdue(cancel_sent_at, now, super::CANCEL_ACK_TIMEOUT) {
let waited =
cancel_sent_at.map_or(Duration::ZERO, |sent| now.saturating_duration_since(sent));
return Err(RemoteError::CancelUnacknowledged(waited));
}
Ok(())
}
fn run(
request: &OneShotRequest,
accumulator: &Arc<Mutex<Accumulator>>,
commands: &mpsc::Receiver<RemoteCommand>,
on_update: &mut dyn FnMut(RemoteUpdate),
liveness_timeout: Duration,
) -> Result<(), RemoteError> {
let request_id = request.request_id();
let (mut stream, welcome) = connect_and_handshake(request.worker())?;
on_update(RemoteUpdate::Connected {
request_id,
info: welcome.clone().into(),
});
let Some(capability) = welcome.render.as_ref() else {
return Err(RemoteError::NoRenderCapacity);
};
{
let (first_sample, samples) = request.range();
let mut acc = accumulator.lock().unwrap_or_else(PoisonError::into_inner);
acc.begin_request_for_range(request_id, first_sample, samples);
}
if request.scene().hdr().is_some() && !capability.hdr {
return Err(RemoteError::HdrUnsupported);
}
request.send(&mut stream, capability)?;
let mut last_event = Instant::now();
let mut seen_first_event = false;
let mut cancel_sent_at: Option<Instant> = None;
loop {
match commands.try_recv() {
Ok(RemoteCommand::Cancel) => {
indicatrix_net::client::send_cancel(&mut stream, request_id)?;
cancel_sent_at.get_or_insert_with(Instant::now);
}
Ok(RemoteCommand::Contribute(sum)) => {
if let OneShotRequest::FinalImage(final_request) = request {
let first = final_request.first_sample + final_request.samples
- final_request.viewer_samples;
let link = super::payload_setting::open_link(&request.worker().address);
indicatrix_net::client::send_contribution_with_link(
&mut stream,
request_id,
(first, final_request.viewer_samples),
(final_request.scene.width, final_request.scene.height),
&sum,
&link,
)?;
last_event = Instant::now();
}
}
Err(TryRecvError::Empty | TryRecvError::Disconnected) => {
}
}
let Some((event, payload)) = try_read_stream_event(&mut stream, super::POLL_INTERVAL)?
else {
let deadline = super::liveness_deadline(seen_first_event, liveness_timeout);
if last_event.elapsed() > deadline {
return Err(RemoteError::WorkerSilent(last_event.elapsed()));
}
ensure_cancel_acknowledged(cancel_sent_at)?;
continue;
};
last_event = Instant::now();
seen_first_event = true;
if let StreamEvent::NeedAsset { content_hash } = &event {
super::asset_upload::send_requested_asset(&mut stream, content_hash)
.map_err(RemoteError::Asset)?;
last_event = Instant::now();
continue;
}
let outcome = {
let mut acc = accumulator.lock().unwrap_or_else(PoisonError::into_inner);
acc.apply(&event, payload.as_deref())
.map_err(|e| RemoteError::Client(e.into()))?
};
let is_terminal = matches!(
outcome,
ApplyOutcome::Done { .. } | ApplyOutcome::WorkerError
);
if let Some(update) = to_remote_update(request_id, &event, outcome) {
on_update(update);
}
if is_terminal {
return Ok(());
}
ensure_cancel_acknowledged(cancel_sent_at)?;
}
}