Drop the per-frame to_vec() clone in the sidecar pixel writer

`LiveOutcome::frame` was carrying the rendered bytes as a fresh
`Vec<u8>` cloned out of `RenderedFrame::rgba_bytes()`. At 60 fps
on a 1080p canvas that was an extra 8 MB allocation + memcpy per
frame on top of the clone `host.last_rendered_frame()` already
paid for. `LiveOutcome` now carries the owned `RenderedFrame`
directly, and `write_outcome` writes its `rgba_bytes()` slice
straight to stdout — same single-clone cost as the host already
incurred, no second allocation.

Small Karpathy-style follow-up from the T8 review ("the host
still copies its rendered buffer to a transient Vec<u8> before
write_all; expose &[u8] straight to write_all once the profile
shows that allocation in the top five"). Profile data is still
deferred (T9 was marked vibe-benchmarking until T10 lands a real
spec), but removing the cheap clone is structurally cleaner and
makes the dispatch path one allocation lighter regardless.

cargo test --bin ely_app: 118 passed, 0 failed, 1 ignored.
cargo test -p ely_servo_host --features servo-engine --test sidecar: 9 passed.
This commit is contained in:
2026-05-10 19:54:14 -04:00
parent 94194e933e
commit e02c0fd502
@@ -136,8 +136,8 @@ fn write_outcome(
let outcome = outcome.unwrap_or_else(|error| LiveOutcome::error(error.to_string())); let outcome = outcome.unwrap_or_else(|error| LiveOutcome::error(error.to_string()));
serde_json::to_writer(&mut *stdout, &outcome.response)?; serde_json::to_writer(&mut *stdout, &outcome.response)?;
stdout.write_all(b"\n")?; stdout.write_all(b"\n")?;
if let Some(frame_bytes) = outcome.frame_bytes { if let Some(frame) = outcome.frame.as_ref() {
stdout.write_all(&frame_bytes)?; stdout.write_all(frame.rgba_bytes())?;
} }
stdout.flush()?; stdout.flush()?;
Ok(()) Ok(())
@@ -271,7 +271,8 @@ fn poll_frame(
let frame = host.last_rendered_frame()?; let frame = host.last_rendered_frame()?;
let has_visible_content = let has_visible_content =
frame.non_white_pixel_count() > 0 && frame.content_pixel_count() > 0; frame.non_white_pixel_count() > 0 && frame.content_pixel_count() > 0;
let outcome = LiveOutcome::frame(LiveFrameReport::new(&snapshot, &frame), &frame); let report = LiveFrameReport::new(&snapshot, &frame);
let outcome = LiveOutcome::from_frame(report, frame);
if has_visible_content { if has_visible_content {
session.awaiting_visible_frame = false; session.awaiting_visible_frame = false;
return Ok(outcome); return Ok(outcome);
@@ -353,28 +354,29 @@ struct LiveSitePermission {
decision: String, decision: String,
} }
/// A handle plus an optional raw-bytes payload, kept together until /// A handle plus an optional rendered frame, kept together until the
/// the moment of writing to stdout. The JSON header advertises /// moment of writing to stdout. The JSON header advertises
/// `rgba_byte_count`; the binary follows on the same pipe. /// `rgba_byte_count`; the binary follows on the same pipe. We carry
/// the `RenderedFrame` (one host-side clone, already paid for inside
/// `host.last_rendered_frame`) instead of doing another `to_vec()`
/// over `rgba_bytes()` — `write_all(&self.rgba_bytes()[..])` writes
/// the existing slice straight to the pipe.
struct LiveOutcome { struct LiveOutcome {
response: LiveResponse, response: LiveResponse,
frame_bytes: Option<Vec<u8>>, frame: Option<RenderedFrame>,
} }
impl LiveOutcome { impl LiveOutcome {
fn empty() -> Self { fn empty() -> Self {
Self { response: LiveResponse::empty(), frame_bytes: None } Self { response: LiveResponse::empty(), frame: None }
} }
fn error(message: String) -> Self { fn error(message: String) -> Self {
Self { response: LiveResponse::error(message), frame_bytes: None } Self { response: LiveResponse::error(message), frame: None }
} }
fn frame(report: LiveFrameReport, frame: &RenderedFrame) -> Self { fn from_frame(report: LiveFrameReport, frame: RenderedFrame) -> Self {
Self { Self { response: LiveResponse::frame(report), frame: Some(frame) }
response: LiveResponse::frame(report),
frame_bytes: Some(frame.rgba_bytes().to_vec()),
}
} }
} }