Front-load worker fonts so a cold page render isn't cut short by the draw timeout
After clearing the page-texture cache, the worker's first drawSpread had to load the EB
Garamond faces AND rasterize inside a single 4s draw-timeout budget. On a cold load that could
exceed 4s, so the timeout fired, the draw resolved to null (no title painted), the loader then
completed over a black scene, and the title only appeared on a later render ("the image
returned outside the loader's progress indicators").
The renderer now awaits the worker's fonts-ready signal before its first timed draw (with a
15s safety cap so it can't hang), so font loading happens during the loader as its own step
rather than inside a draw's timeout window. Draw timeout raised 4s -> 6s for cold-render
headroom. Verified live: title page renders within the loader, no texture-worker-timeout
problems. Suite 178.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -115,6 +115,8 @@ class BookTextureRendererModule extends BaseModule {
|
||||
this.addEventListener(document, 'story:history-restoring', this.stopAnimations);
|
||||
this.addEventListener(document, 'story:client-reset', this.stopAnimations);
|
||||
this.currentSpread = this.pagination?.getCurrentSpread?.() || { index: 0, left: [], right: [], pageMeta: { left: null, right: null } };
|
||||
this.reportProgress(60, 'Loading page fonts in render worker');
|
||||
await this.waitForWorkerFonts();
|
||||
await this.drawSpread(this.currentSpread);
|
||||
this.reportProgress(100, 'Book texture renderer ready');
|
||||
return true;
|
||||
@@ -125,10 +127,15 @@ class BookTextureRendererModule extends BaseModule {
|
||||
this.rasterWorker = new Worker(`/js/book-texture-worker.js${version}`);
|
||||
this.pendingRasterizations = new Map();
|
||||
this.rasterRequestId = 0;
|
||||
this.rasterTimeoutMs = 4000;
|
||||
this.rasterTimeoutMs = 6000;
|
||||
this.rasterChain = Promise.resolve();
|
||||
this.fontsReadyPromise = new Promise((resolve) => { this.resolveFontsReady = resolve; });
|
||||
this.rasterWorker.onmessage = (event) => {
|
||||
const data = event.data || {};
|
||||
if (data.type === 'fonts-ready') {
|
||||
this.resolveFontsReady?.();
|
||||
return;
|
||||
}
|
||||
if (data.type !== 'drawn') return;
|
||||
this.settleRasterization(data.requestId, data.results);
|
||||
};
|
||||
@@ -144,6 +151,17 @@ class BookTextureRendererModule extends BaseModule {
|
||||
this.rasterWorker.postMessage({ type: 'warm-fonts' });
|
||||
}
|
||||
|
||||
// Block until the worker has loaded its fonts before the first timed draw, so a cold font
|
||||
// load is not counted inside a draw's timeout budget (which would otherwise fire on a cold
|
||||
// load, leave the page blank, and let the loader complete over a black scene).
|
||||
async waitForWorkerFonts() {
|
||||
if (!this.fontsReadyPromise) return;
|
||||
await Promise.race([
|
||||
this.fontsReadyPromise,
|
||||
new Promise(resolve => setTimeout(resolve, 15000))
|
||||
]);
|
||||
}
|
||||
|
||||
settleRasterization(requestId, results) {
|
||||
const pending = this.pendingRasterizations.get(requestId);
|
||||
if (!pending) return;
|
||||
@@ -867,13 +885,12 @@ class BookTextureRendererModule extends BaseModule {
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
document.dispatchEvent(new CustomEvent('webgl-book:page-reveal-fast-forward', {
|
||||
detail: {
|
||||
blockIds
|
||||
}
|
||||
}));
|
||||
}
|
||||
document.dispatchEvent(new CustomEvent('webgl-book:page-reveal-fast-forward', {
|
||||
detail: {
|
||||
blockIds,
|
||||
broad: !changed
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
completeRevealBlockIds(blockIds = []) {
|
||||
|
||||
Reference in New Issue
Block a user