ElyShow: show layers come one per reply, so large slides play (R2.20)
This commit is contained in:
5 files changed
+68
-10
No files matched your search
@@ -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).
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
Reference in new issue
Block a user