Fix WebGL page cache and flip sequencing
This commit is contained in:
@@ -38,9 +38,11 @@ class BookTextureRendererModule extends BaseModule {
|
||||
this.lastDrawSkipLoggedAt = 0;
|
||||
this.animationFrameId = null;
|
||||
this.lastAnimationFrameAt = 0;
|
||||
this.targetFrameDurationMs = 1000 / 30;
|
||||
this.targetFrameDurationMs = 1000 / 60;
|
||||
this.pipelineTimings = [];
|
||||
this.imageCache = new Map();
|
||||
this.pendingPageCacheWrites = new Map();
|
||||
this.pageContentVersions = new Map();
|
||||
|
||||
this.bindMethods([
|
||||
'initialize',
|
||||
@@ -84,6 +86,8 @@ class BookTextureRendererModule extends BaseModule {
|
||||
'tickAnimations',
|
||||
'publishSpread',
|
||||
'cachePublishedPages',
|
||||
'getPageCacheWriteKey',
|
||||
'isOlderPageMeta',
|
||||
'schedulePageCacheWrite',
|
||||
'getPageCanvas',
|
||||
'getHitMap',
|
||||
@@ -641,7 +645,7 @@ class BookTextureRendererModule extends BaseModule {
|
||||
});
|
||||
this.pendingRevealBlockIds.delete(String(blockId));
|
||||
this.revealPublishBlockIds = new Set([String(blockId)]);
|
||||
this.drawSpread(this.currentSpread || this.pagination?.getCurrentSpread?.(), this.getBlockSides(blockId));
|
||||
this.drawSpread(this.currentSpread || this.pagination?.getCurrentSpread?.(), ['left', 'right']);
|
||||
document.dispatchEvent(new CustomEvent('webgl-book:page-reveal-start', {
|
||||
detail: {
|
||||
blockId
|
||||
@@ -692,7 +696,7 @@ class BookTextureRendererModule extends BaseModule {
|
||||
this.pendingRevealBlockIds.delete(id);
|
||||
this.revealPublishBlockIds = new Set([id]);
|
||||
const spread = detail.spread || this.currentSpread || this.pagination?.getCurrentSpread?.();
|
||||
const sides = this.getBlockSides(blockId);
|
||||
const sides = ['left', 'right'];
|
||||
const published = this.drawSpread(spread, sides, { preloadOnly });
|
||||
if (preloadOnly && published) {
|
||||
this.preparedRevealCache.set(id, {
|
||||
@@ -728,6 +732,7 @@ class BookTextureRendererModule extends BaseModule {
|
||||
left: prepared.left || null,
|
||||
right: prepared.right || null,
|
||||
reveal: prepared.reveal || {},
|
||||
pageMeta: prepared.pageMeta || {},
|
||||
preparedFromCache: true
|
||||
}
|
||||
}));
|
||||
@@ -856,7 +861,7 @@ class BookTextureRendererModule extends BaseModule {
|
||||
metrics: this.metrics,
|
||||
hitMaps: this.hitMaps,
|
||||
sides: sidesToPublish,
|
||||
pageMeta: this.currentSpread?.pageMeta || {}
|
||||
pageMeta: this.buildPublishPageMeta(sidesToPublish)
|
||||
};
|
||||
if (options.preloadOnly) detail.preloadOnly = true;
|
||||
if (sidesToPublish.includes('left')) {
|
||||
@@ -907,6 +912,35 @@ class BookTextureRendererModule extends BaseModule {
|
||||
return detail;
|
||||
}
|
||||
|
||||
buildPublishPageMeta(sides = []) {
|
||||
const baseMeta = this.currentSpread?.pageMeta || {};
|
||||
return sides.reduce((meta, side) => {
|
||||
const source = baseMeta[side] || null;
|
||||
if (!source) {
|
||||
meta[side] = null;
|
||||
return meta;
|
||||
}
|
||||
const lines = Array.isArray(this.currentSpread?.[side]) ? this.currentSpread[side] : [];
|
||||
const maxBlockId = lines.reduce((max, line) => Math.max(max, Number(line?.blockId || 0)), 0);
|
||||
const lineCount = lines.length;
|
||||
const pageIndex = Number(source.pageIndex);
|
||||
const key = Number.isFinite(pageIndex) ? pageIndex : side;
|
||||
const nextVersion = Math.max(1, Number(this.pageContentVersions.get(key) || 0) + 1);
|
||||
this.pageContentVersions.set(key, nextVersion);
|
||||
meta[side] = {
|
||||
...source,
|
||||
contentVersion: nextVersion,
|
||||
completenessScore: (maxBlockId * 1000) + lineCount,
|
||||
maxBlockId,
|
||||
lineCount
|
||||
};
|
||||
return meta;
|
||||
}, {
|
||||
left: Object.prototype.hasOwnProperty.call(baseMeta, 'left') ? baseMeta.left : null,
|
||||
right: Object.prototype.hasOwnProperty.call(baseMeta, 'right') ? baseMeta.right : null
|
||||
});
|
||||
}
|
||||
|
||||
cachePublishedPages(sides = [], detail = {}) {
|
||||
if (!this.pageCache || typeof this.pageCache.cachePageCanvas !== 'function') return;
|
||||
sides.forEach((side) => {
|
||||
@@ -919,10 +953,66 @@ class BookTextureRendererModule extends BaseModule {
|
||||
|
||||
schedulePageCacheWrite(pageMeta, canvas) {
|
||||
const frozenCanvas = this.cloneCanvas(canvas);
|
||||
const scheduler = window.requestIdleCallback || ((callback) => window.setTimeout(callback, 16));
|
||||
scheduler(() => {
|
||||
this.pageCache?.cachePageCanvas?.(pageMeta, frozenCanvas);
|
||||
}, { timeout: 250 });
|
||||
const key = this.getPageCacheWriteKey(pageMeta, frozenCanvas);
|
||||
const pending = this.pendingPageCacheWrites.get(key);
|
||||
if (pending && this.isOlderPageMeta(pageMeta, pending.pageMeta)) return pending.promise;
|
||||
const previousWrite = pending?.promise || Promise.resolve();
|
||||
const write = previousWrite.catch(() => false).then(() => this.pageCache?.cachePageCanvas?.(pageMeta, frozenCanvas))
|
||||
.then((stored) => {
|
||||
if (!stored) {
|
||||
document.dispatchEvent(new CustomEvent('webgl-book:page-cache-problem', {
|
||||
detail: {
|
||||
type: 'db-write-failed',
|
||||
pageIndex: pageMeta?.pageIndex ?? null,
|
||||
key
|
||||
}
|
||||
}));
|
||||
}
|
||||
return stored;
|
||||
})
|
||||
.catch((error) => {
|
||||
document.dispatchEvent(new CustomEvent('webgl-book:page-cache-problem', {
|
||||
detail: {
|
||||
type: 'db-write-error',
|
||||
pageIndex: pageMeta?.pageIndex ?? null,
|
||||
key,
|
||||
message: error?.message || String(error)
|
||||
}
|
||||
}));
|
||||
return false;
|
||||
})
|
||||
.finally(() => {
|
||||
if (this.pendingPageCacheWrites.get(key)?.promise === write) {
|
||||
this.pendingPageCacheWrites.delete(key);
|
||||
}
|
||||
});
|
||||
this.pendingPageCacheWrites.set(key, {
|
||||
promise: write,
|
||||
pageMeta: { ...(pageMeta || {}) }
|
||||
});
|
||||
return write;
|
||||
}
|
||||
|
||||
isOlderPageMeta(incoming = {}, existing = null) {
|
||||
if (!existing) return false;
|
||||
const incomingCompleteness = Math.max(0, Number(incoming?.completenessScore || 0));
|
||||
const existingCompleteness = Math.max(0, Number(existing?.completenessScore || 0));
|
||||
if (incomingCompleteness < existingCompleteness) return true;
|
||||
if (incomingCompleteness > existingCompleteness) return false;
|
||||
const incomingVersion = Math.max(0, Number(incoming?.contentVersion || 0));
|
||||
const existingVersion = Math.max(0, Number(existing?.contentVersion || 0));
|
||||
return incomingVersion > 0 && existingVersion > incomingVersion;
|
||||
}
|
||||
|
||||
getPageCacheWriteKey(pageMeta = {}, canvas = null) {
|
||||
if (this.pageCache && typeof this.pageCache.makePageKey === 'function') {
|
||||
return this.pageCache.makePageKey({
|
||||
...pageMeta,
|
||||
width: canvas?.width ?? pageMeta.width,
|
||||
height: canvas?.height ?? pageMeta.height
|
||||
});
|
||||
}
|
||||
return `${pageMeta.cacheKey || window.MODULE_CACHE_BUSTER || 'dev'}:page:${pageMeta.pageIndex}:${canvas?.width || pageMeta.width}x${canvas?.height || pageMeta.height}`;
|
||||
}
|
||||
|
||||
getPageCanvas(side) {
|
||||
|
||||
Reference in New Issue
Block a user