Stabilize WebGL reveal timing
This commit is contained in:
@@ -13,6 +13,48 @@ const uiDisplayHandlerPath = path.join(__dirname, '..', 'public', 'js', 'ui-disp
|
||||
const uiDisplayHandlerSource = fs.readFileSync(uiDisplayHandlerPath, 'utf8');
|
||||
const bookPaginationPath = path.join(__dirname, '..', 'public', 'js', 'book-pagination-module.js');
|
||||
const bookPaginationSource = fs.readFileSync(bookPaginationPath, 'utf8');
|
||||
const webglScenePath = path.join(__dirname, '..', 'public', 'js', 'webgl-book-scene-module.js');
|
||||
const webglSceneSource = fs.readFileSync(webglScenePath, 'utf8');
|
||||
const loaderPath = path.join(__dirname, '..', 'public', 'js', 'loader.js');
|
||||
const loaderSource = fs.readFileSync(loaderPath, 'utf8');
|
||||
|
||||
function dependencyList(source, moduleId) {
|
||||
const classStart = source.indexOf(`super('${moduleId}'`);
|
||||
if (classStart < 0) return [];
|
||||
const dependencyMatch = source.slice(classStart).match(/this\.dependencies\s*=\s*\[([^\]]*)\]/);
|
||||
if (!dependencyMatch) return [];
|
||||
return Array.from(dependencyMatch[1].matchAll(/'([^']+)'|"([^"]+)"/g)).map(match => match[1] || match[2]);
|
||||
}
|
||||
|
||||
function directGetModules(source) {
|
||||
return Array.from(source.matchAll(/getModule\('([^']+)'\)|getModule\("([^"]+)"\)/g)).map(match => match[1] || match[2]);
|
||||
}
|
||||
|
||||
function undeclaredDirectDependencies(source, moduleId, optional = []) {
|
||||
const declared = new Set(dependencyList(source, moduleId));
|
||||
const allowed = new Set([moduleId, ...declared, ...optional]);
|
||||
return Array.from(new Set(directGetModules(source))).filter(id => !allowed.has(id));
|
||||
}
|
||||
|
||||
function cacheBuster(source) {
|
||||
return source.match(/MODULE_CACHE_BUSTER\s*=\s*'([^']+)'/)?.[1] || null;
|
||||
}
|
||||
|
||||
function methodBody(source, methodName) {
|
||||
const start = source.indexOf(`${methodName}(`);
|
||||
if (start < 0) return '';
|
||||
const braceStart = source.indexOf('{', start);
|
||||
if (braceStart < 0) return '';
|
||||
let depth = 0;
|
||||
for (let index = braceStart; index < source.length; index += 1) {
|
||||
if (source[index] === '{') depth += 1;
|
||||
if (source[index] === '}') {
|
||||
depth -= 1;
|
||||
if (depth === 0) return source.slice(braceStart + 1, index);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
const checks = [
|
||||
['scene-level SSAO import', /SSAOPass/.test(source)],
|
||||
@@ -46,7 +88,24 @@ const checks = [
|
||||
['page reveal shader uses coordinate mask instead of comparing page textures', /bookRevealWordRects/.test(source) && /bookRevealWordTimings/.test(source) && /bookRevealElapsedMs/.test(source) && !/texture2D\(bookRevealMap/.test(source)],
|
||||
['texture renderer explicitly gates initial font before painting', /waitForTextureFonts/.test(textureRendererSource) && /ensureTextureFontFace/.test(textureRendererSource) && /FontFace\(family/.test(textureRendererSource) && /document\.fonts\.load\('72px "EB Garamond Initials"'\)/.test(textureRendererSource)],
|
||||
['texture renderer no longer republishes stale scene-ready textures', !/addEventListener\(document, 'webgl-book:scene-ready'/.test(textureRendererSource) && !/handleSceneReady\(\)\s*{\s*this\.publishSpread\(\)/.test(textureRendererSource) && !/drawEmptySpread/.test(textureRendererSource)],
|
||||
['prepared reveal never falls back to unmasked direct upload before shader compile', /pendingPageReveal/.test(source) && /applyPendingPageReveal/.test(source) && !/if \(!shader\?\.uniforms\) {\s*uploadPageTextureDirect\(side, sourceCanvas\)/.test(source)]
|
||||
['prepared reveal never falls back to unmasked direct upload before shader compile', /pendingPageReveal/.test(source) && /applyPendingPageReveal/.test(source) && !/if \(!shader\?\.uniforms\) {\s*uploadPageTextureDirect\(side, sourceCanvas\)/.test(source)],
|
||||
['ui display handler declares every direct module lookup', undeclaredDirectDependencies(uiDisplayHandlerSource, 'ui-display-handler').length === 0],
|
||||
['webgl scene declares every direct module lookup', undeclaredDirectDependencies(webglSceneSource, 'webgl-book-scene').length === 0],
|
||||
['loader cache key matches webgl procedural imports', cacheBuster(loaderSource) && source.includes(`procedural-book-model.js?v=${cacheBuster(loaderSource)}`) && proceduralBookSource.length > 0],
|
||||
['webgl lab exposes loader timing diagnostics', /loaderTimings/.test(source) && /markLoaderTiming/.test(source) && /primeSceneForLoader/.test(source)],
|
||||
['webgl lab records shader compile timing during loader prime', /markLoaderTiming\('shaderCompile:start'\)/.test(source) && /renderer\.compile\(scene, camera\)/.test(source) && /markLoaderTiming\('shaderCompile:end'\)/.test(source)],
|
||||
['webgl lab exposes reveal uniform diagnostics', /getRevealDebugState/.test(source) && /bookRevealActive/.test(source) && /bookRevealElapsedMs/.test(source) && /bookRevealWordCount/.test(source)],
|
||||
['webgl lab records page reveal clear reasons', /clearPageReveal\(side, reason/.test(source) && /webglRevealClearLog/.test(source)],
|
||||
['webgl reveal clock starts on first render frame', /pendingStart/.test(source) && /state\.pendingStart/.test(source) && /state\.startedAt = now/.test(source)],
|
||||
['webgl reveal visual clock caps missed-frame deltas', /visualElapsedMs/.test(source) && /revealFrameDeltaMs/.test(source) && /Math\.min\(revealFrameDeltaMs/.test(source)],
|
||||
['webgl lab records page texture upload/copy timings', /pageTextureTimings/.test(source) && /markPageTextureTiming/.test(source) && /webglPageTextureTimings/.test(source)],
|
||||
['webgl lab times direct and reveal texture uploads', /markPageTextureTiming\('directUpload:start'/.test(source) && /markPageTextureTiming\('revealUpload:start'/.test(source) && /markPageTextureTiming\('drawCanvasPageTexture:start'/.test(source)],
|
||||
['texture renderer exposes reveal pipeline diagnostics', /pipelineTimings/.test(textureRendererSource) && /markPipelineTiming/.test(textureRendererSource) && /webglTexturePipeline/.test(textureRendererSource)],
|
||||
['texture renderer records prepare draw publish and start reveal timing', /markPipelineTiming\('prepareRevealBlock:start'/.test(textureRendererSource) && /markPipelineTiming\('drawSpread:start'/.test(textureRendererSource) && /markPipelineTiming\('publishSpread'/.test(textureRendererSource) && /markPipelineTiming\('startPreparedRevealAnimation'/.test(textureRendererSource)],
|
||||
['texture renderer diagnostics include reveal word counts', /wordCounts/.test(textureRendererSource) && /revealWords/.test(textureRendererSource) && /wordRects/.test(textureRendererSource)],
|
||||
['webgl scene avoids duplicate initial texture publish', !/this\.triggerTextureRefresh\(\)/.test(methodBody(webglSceneSource, 'initializeScene'))],
|
||||
['webgl scene does not republish 3D page textures from DOM refresh events', !/addEventListener\(document, 'story:turn-start', this\.triggerTextureRefresh\)/.test(webglSceneSource) && !/addEventListener\(document, 'story:turn-complete', this\.triggerTextureRefresh\)/.test(webglSceneSource) && !/addEventListener\(document, 'story:history-updated', this\.triggerTextureRefresh\)/.test(webglSceneSource) && !/addEventListener\(document, 'input', this\.triggerTextureRefresh/.test(webglSceneSource) && !/addEventListener\(document, 'change', this\.triggerTextureRefresh/.test(webglSceneSource)],
|
||||
['webgl scene adoptPageContent does not republish 3D page textures', !/triggerTextureRefresh/.test(methodBody(webglSceneSource, 'adoptPageContent'))]
|
||||
];
|
||||
|
||||
const failures = checks.filter(([, passed]) => !passed).map(([name]) => name);
|
||||
|
||||
Reference in New Issue
Block a user