ElyShow: show layers come one per reply, so large slides play (R2.20)

This commit is contained in:
ZacharyZhang-NY committed 2026-10-09 02:31:47 -04:00
1 parent 56855acfb4
commit 48e83a9b45
5 files changed
+68 -10

No files matched your search

+15 -1
View File
@@ -180,7 +180,21 @@ impl Doc {
.u64(u64::from(pixels.0))
.u64(u64::from(pixels.1))
.bytes();
crate::show::layers(&worker.request(tag::SLIDE_LAYERS, &request)?)
let head = worker.request(tag::SLIDE_LAYERS, &request)?;
let mut unpack = Unpack::new(&head);
let (width, height, count) = (unpack.u64()?, unpack.u64()?, unpack.u64()?);
// One layer per reply keeps each frame small.
let mut whole = Pack::new().u64(width).u64(height).u64(count);
for index in 0..count {
let (json, painted) = (unpack.str()?, unpack.u64()?);
whole = whole.str(&json).u64(painted);
if painted != 0 {
let id = worker.doc_id(self)?;
let reply = worker.request(tag::SLIDE_LAYER, &Pack::new().u64(id).u64(index).bytes())?;
whole = whole.blob(&Unpack::new(&reply).blob()?);
}
}
crate::show::layers(&whole.bytes())
}
/// Saves to `url` as `format`; failure surfaces (LOK-09).
+18
View File
@@ -220,3 +220,21 @@ fn an_animated_object_is_its_own_layer_under_its_effect_hash() {
#[test]
fn layers_larger_than_one_frame_arrive_one_by_one() {
if install().is_none() {
eprintln!("SKIP: no pinned LOK build");
return;
}
let (_dir, mut worker, doc) = open_deck();
worker.run(&doc, "draw.shape", &["rectangle", "true"]).expect("draws");
let json = serde_json::json!({ "preset": "ooo-entrance-fade-in" }).to_string();
worker.run(&doc, "animation.add", &[&json]).expect("adds");
// Three layers at this size pass one 64 MB frame.
let layers = doc.slide_layers(&mut worker, 0, (5000, 2812)).expect("layers");
let (w, h) = layers.size;
let bytes = w as usize * h as usize * 4;
assert!(layers.list.len() * bytes > crate::ipc::MAX_FRAME, "{} layers of {bytes}", layers.list.len());
assert!(layers.list.iter().all(|layer| layer.pixels.len() == bytes), "every layer arrives whole");
}
+2
View File
@@ -86,6 +86,8 @@ pub mod tag {
/// Refuses unless the document is still at a revision.
pub const EXPECT: u8 = 33;
pub const REVISION: u8 = 34;
/// One layer's pixels from the last `SLIDE_LAYERS`.
pub const SLIDE_LAYER: u8 = 35;
pub const OK: u8 = 100;
pub const ERR: u8 = 101;
}
+1
View File
@@ -205,6 +205,7 @@ fn answer(lok: &Lok, tag: u8, id: u64, payload: &[u8]) {
tag::PAINT_TILE => input::painted(unpack),
tag::PAINT_PART => input::part_painted(unpack),
tag::SLIDE_LAYERS => slides::slide_layers(unpack),
tag::SLIDE_LAYER => slides::slide_layer(unpack),
tag::VERIFY => unpack
.str()
.and_then(|url| verify(lok, &url))
+32 -9
View File
@@ -1,9 +1,17 @@
use std::io;
use std::{cell::RefCell, collections::HashMap, io};
use super::with_doc;
use crate::ipc::{self, Pack, Unpack};
use crate::ipc::{Pack, Unpack};
/// Slide `part`'s slideshow layers at about the asked pixels.
/// The most a slide's layers may take in memory.
const LAYER_MEMORY: usize = 512 * 1024 * 1024;
// Rendered layers wait here; each leaves on its own request.
thread_local! {
static WAITING: RefCell<HashMap<u64, Vec<Option<Vec<u8>>>>> = RefCell::new(HashMap::new());
}
/// Slide `part`'s layers, its pixels held back.
pub(super) fn slide_layers(unpack: &mut Unpack) -> io::Result<Vec<u8>> {
let (id, part) = (unpack.u64()?, unpack.u64()?);
let (width, height) = (unpack.u64()?, unpack.u64()?);
@@ -20,18 +28,33 @@ pub(super) fn slide_layers(unpack: &mut Unpack) -> io::Result<Vec<u8>> {
let hash = slide
.and_then(|slide| slide["hash"].as_str())
.ok_or_else(|| bad("no such slide"))?;
let ((width, height), layers) =
doc.slide_layers((part, hash), (width, height), ipc::MAX_FRAME - 1024)?;
let ((width, height), layers) = doc.slide_layers((part, hash), (width, height), LAYER_MEMORY)?;
let mut body = Pack::new()
.u64(u64::from(width))
.u64(u64::from(height))
.u64(layers.len() as u64);
for layer in &layers {
let mut pixels = Vec::with_capacity(layers.len());
for layer in layers {
body = body.str(&layer.json).u64(u64::from(layer.pixels.is_some()));
if let Some(pixels) = &layer.pixels {
body = body.blob(pixels);
}
pixels.push(layer.pixels);
}
WAITING.with(|waiting| waiting.borrow_mut().insert(id, pixels));
Ok(body.bytes())
})?
}
/// Hands over layer `index`'s pixels once, then frees them.
pub(super) fn slide_layer(unpack: &mut Unpack) -> io::Result<Vec<u8>> {
let (id, index) = (unpack.u64()?, unpack.u64()?);
let taken = WAITING.with(|waiting| {
let mut waiting = waiting.borrow_mut();
let layers = waiting.get_mut(&id)?;
let pixels = layers.get_mut(usize::try_from(index).ok()?)?.take();
if layers.iter().all(Option::is_none) {
waiting.remove(&id);
}
pixels
});
let pixels = taken.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, format!("no layer {index} waits")))?;
Ok(Pack::new().blob(&pixels).bytes())
}