mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-27 19:47:12 -06:00
M13.30: web depth and polish
- GPU in browser job workers: each job worker opens its own WebGPU device (deferred readbacks); the Render Queue export and the analyses' frame loops are futures (render::passes, offload::run_request_async) whose frames render in passes, awaiting the device between them. GPU particles and Advanced 3D read back under keys too. workers.jobs reports passes and readbacks; the smoke test measures them. - Layer buffers in the browser's OPFS disk cache: frame workers back their layer caches with a PrefetchStore; the page plans what to fetch before a request from recent misses (LayerPrefetch over the shared DiskIndex). - Templates embed footage (default on, size-capped with a warning) and creating from a template extracts it next to the new project / into browser storage. - ICC LUT profiles (lut8/lut16/lutAtoB/lutBtoA, from the public ICC spec) in Simulate Output > My Custom RGB, baked into a 3D LUT for the viewer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d39c0e818c
commit
f3715aac32
@@ -173,6 +173,38 @@ impl DiskIndex {
|
||||
}
|
||||
}
|
||||
|
||||
/// Which layer buffers a browser frame worker should fetch from the disk cache before its next
|
||||
/// frame (its lookups mid-render are synchronous, the Origin Private File System is not): the
|
||||
/// layer-cache misses the workers reported for recent frames, most recent first, that are on
|
||||
/// disk ([`DiskIndex`], kind [`Kind::Layer`]).
|
||||
#[derive(Default)]
|
||||
pub struct LayerPrefetch {
|
||||
recent: std::collections::VecDeque<u128>,
|
||||
}
|
||||
|
||||
/// Misses [`LayerPrefetch`] remembers.
|
||||
const PREFETCH_RECENT: usize = 512;
|
||||
|
||||
impl LayerPrefetch {
|
||||
/// A frame's layer-cache misses (salted keys).
|
||||
pub fn note_misses(&mut self, keys: impl IntoIterator<Item = u128>) {
|
||||
for k in keys {
|
||||
self.recent.retain(|x| *x != k);
|
||||
self.recent.push_front(k);
|
||||
}
|
||||
self.recent.truncate(PREFETCH_RECENT);
|
||||
}
|
||||
|
||||
/// Up to `max` keys to fetch before the next frame.
|
||||
pub fn plan(&self, index: &DiskIndex, max: usize) -> Vec<u128> {
|
||||
self.recent.iter().copied().filter(|k| index.contains(Kind::Layer, *k)).take(max).collect()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.recent.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
|
||||
enum Job {
|
||||
Write(Kind, u128, Vec<u8>),
|
||||
@@ -584,6 +616,29 @@ pub fn read_frame_entry(data: &[u8]) -> Option<Frame8> {
|
||||
decode_frame(verify(data, Kind::Frame)?)
|
||||
}
|
||||
|
||||
/// A sealed layer-buffer entry: what the browser's frame workers write to the Origin Private
|
||||
/// File System (`layers/<32 hex>.ecc`).
|
||||
pub fn layer_entry(buf: &Buf) -> Vec<u8> {
|
||||
seal(Kind::Layer, &encode_layer(buf))
|
||||
}
|
||||
|
||||
/// A layer buffer from a sealed entry's bytes (`None` when damaged).
|
||||
pub fn read_layer_entry(data: &[u8]) -> Option<Buf> {
|
||||
decode_layer(verify(data, Kind::Layer)?)
|
||||
}
|
||||
|
||||
impl crate::cache::LayerStore for DiskCache {
|
||||
fn get_layer(&self, key: u128) -> Option<Arc<Buf>> {
|
||||
DiskCache::get_layer(self, key).map(Arc::new)
|
||||
}
|
||||
fn put_layer(&self, key: u128, buf: &Buf) {
|
||||
DiskCache::put_layer(self, key, buf);
|
||||
}
|
||||
fn min_layer_ms(&self) -> u64 {
|
||||
DiskCache::min_layer_ms(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// An entry's file name in its kind's folder (`<32 hex>.ecc`).
|
||||
pub fn entry_name(key: u128) -> String {
|
||||
format!("{key:032x}.ecc")
|
||||
|
||||
Reference in New Issue
Block a user