@editframe/elements 0.40.1-beta.0 → 0.40.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -42,8 +42,8 @@ declare class EFFramegen {
42
42
  private logSequence;
43
43
  frameTasksInProgress: boolean;
44
44
  currentFrameNumber: number;
45
- private seekTimingAccum;
46
- private seekTimingCount;
45
+ private timingFrameCount;
46
+ private timingAccum;
47
47
  trace(...args: any[]): void;
48
48
  syncLog(...args: any[]): Promise<void>;
49
49
  private initializeVerificationCanvas;
@@ -91,7 +91,8 @@ var EFFramegen = class {
91
91
  this.logSequence = 0;
92
92
  this.frameTasksInProgress = false;
93
93
  this.currentFrameNumber = 0;
94
- this.seekTimingAccum = {
94
+ this.timingFrameCount = 0;
95
+ this.timingAccum = {
95
96
  updateComplete1Ms: 0,
96
97
  updateComplete2Ms: 0,
97
98
  updateComplete3Ms: 0,
@@ -104,7 +105,6 @@ var EFFramegen = class {
104
105
  frameTasksMs: 0,
105
106
  totalMs: 0
106
107
  };
107
- this.seekTimingCount = 0;
108
108
  this.BRIDGE = window.FRAMEGEN_BRIDGE;
109
109
  if (this.BRIDGE) this.connectToBridge();
110
110
  }
@@ -221,34 +221,14 @@ var EFFramegen = class {
221
221
  if (!firstGroup) throw new Error("No temporal elements found");
222
222
  const frameTime = this.renderOptions.encoderOptions.fromMs + frameNumber * this.frameDurationMs;
223
223
  const frameTimeMs = Number(Number(frameTime).toFixed(5));
224
- const seekTiming = await firstGroup.seekForRender(frameTimeMs);
225
- this.seekTimingAccum.updateComplete1Ms += seekTiming.updateComplete1Ms;
226
- this.seekTimingAccum.updateComplete2Ms += seekTiming.updateComplete2Ms;
227
- this.seekTimingAccum.updateComplete3Ms += seekTiming.updateComplete3Ms;
228
- this.seekTimingAccum.textSegmentsMs += seekTiming.textSegmentsMs;
229
- this.seekTimingAccum.renderFrameMs += seekTiming.renderFrameMs;
230
- this.seekTimingAccum.renderFrameQueryMs += seekTiming.renderFrameQueryMs;
231
- this.seekTimingAccum.renderFramePrepareMs += seekTiming.renderFramePrepareMs;
232
- this.seekTimingAccum.renderFrameDrawMs += seekTiming.renderFrameDrawMs;
233
- this.seekTimingAccum.renderFrameAnimsMs += seekTiming.renderFrameAnimsMs;
234
- this.seekTimingAccum.frameTasksMs += seekTiming.frameTasksMs;
235
- this.seekTimingAccum.totalMs += seekTiming.totalMs;
236
- this.seekTimingCount++;
237
- if (this.seekTimingCount % 30 === 0) {
238
- const n = this.seekTimingCount;
239
- console.log("[EF_FRAMEGEN] seekForRender phase avg (ms) over", n, "frames:", JSON.stringify({
240
- uc1: (this.seekTimingAccum.updateComplete1Ms / n).toFixed(2),
241
- uc2: (this.seekTimingAccum.updateComplete2Ms / n).toFixed(2),
242
- uc3: (this.seekTimingAccum.updateComplete3Ms / n).toFixed(2),
243
- textSegments: (this.seekTimingAccum.textSegmentsMs / n).toFixed(2),
244
- renderFrame: (this.seekTimingAccum.renderFrameMs / n).toFixed(2),
245
- rf_query: (this.seekTimingAccum.renderFrameQueryMs / n).toFixed(2),
246
- rf_prepare: (this.seekTimingAccum.renderFramePrepareMs / n).toFixed(2),
247
- rf_draw: (this.seekTimingAccum.renderFrameDrawMs / n).toFixed(2),
248
- rf_anims: (this.seekTimingAccum.renderFrameAnimsMs / n).toFixed(2),
249
- frameTasks: (this.seekTimingAccum.frameTasksMs / n).toFixed(2),
250
- total: (this.seekTimingAccum.totalMs / n).toFixed(2)
251
- }));
224
+ const timing = await firstGroup.seekForRender(frameTimeMs);
225
+ this.timingFrameCount++;
226
+ for (const key of Object.keys(this.timingAccum)) this.timingAccum[key] += timing[key];
227
+ if (this.timingFrameCount >= 30) {
228
+ const n = this.timingFrameCount;
229
+ console.log(`[EF_FRAMEGEN] seekForRender phase avg (${n} frames):`, `total=${(this.timingAccum.totalMs / n).toFixed(1)}ms`, `uc1=${(this.timingAccum.updateComplete1Ms / n).toFixed(1)}ms`, `uc2=${(this.timingAccum.updateComplete2Ms / n).toFixed(1)}ms`, `uc3=${(this.timingAccum.updateComplete3Ms / n).toFixed(1)}ms`, `text=${(this.timingAccum.textSegmentsMs / n).toFixed(1)}ms`, `renderFrame=${(this.timingAccum.renderFrameMs / n).toFixed(1)}ms`, `rf.query=${(this.timingAccum.renderFrameQueryMs / n).toFixed(1)}ms`, `rf.prepare=${(this.timingAccum.renderFramePrepareMs / n).toFixed(1)}ms`, `rf.draw=${(this.timingAccum.renderFrameDrawMs / n).toFixed(1)}ms`, `rf.anims=${(this.timingAccum.renderFrameAnimsMs / n).toFixed(1)}ms`, `frameTasks=${(this.timingAccum.frameTasksMs / n).toFixed(1)}ms`);
230
+ this.timingFrameCount = 0;
231
+ for (const key of Object.keys(this.timingAccum)) this.timingAccum[key] = 0;
252
232
  }
253
233
  if (this.showFrameBox) this.frameBox.innerHTML = `
254
234
  <div>🖼️ Frame: ${frameNumber}</div>
@@ -1 +1 @@
1
- {"version":3,"file":"EF_FRAMEGEN.js","names":[],"sources":["../src/EF_FRAMEGEN.ts"],"sourcesContent":["import type { VideoRenderOptions } from \"@editframe/assets\";\n\nimport {\n shallowGetTimegroups,\n type SeekForRenderTiming,\n} from \"./elements/EFTimegroup.js\";\nimport { setupTemporalHierarchy } from \"./elements/setupTemporalHierarchy.js\";\n\nimport { setupBrowserTracing } from \"./otel/setupBrowserTracing.js\";\nimport {\n clearCurrentFrameSpan,\n enableTracing,\n extractParentContext,\n setCurrentFrameSpan,\n type TraceContext,\n withSpan,\n withSpanAndContext,\n} from \"./otel/tracingHelpers.js\";\n\ninterface Bridge {\n onInitialize: (\n callback: (\n renderOptions: VideoRenderOptions,\n traceContext?: TraceContext,\n otelEndpoint?: string,\n ) => void,\n ) => void;\n\n initialized(): void;\n\n onBeginFrame(\n callback: (\n frameNumber: number,\n isLast: boolean,\n traceContext?: TraceContext,\n ) => void,\n ): void;\n\n onTriggerCanvas(callback: (traceContext?: TraceContext) => void): void;\n\n frameReady(frameNumber: number, audioSamples: ArrayBuffer): void;\n\n error(error: Error): void;\n\n syncLog(sequence: number, message: string, callback: () => void): void;\n\n exportSpans?: (endpoint: string, payload: string) => void;\n}\n\ndeclare global {\n interface Window {\n EF_FRAMEGEN?: EFFramegen;\n FRAMEGEN_BRIDGE?: Bridge;\n FRAMEGEN_BINDING?: any;\n FRAMEGEN_BINDING_error?: (error: Error) => void;\n EF_RENDERING?: () => boolean;\n }\n}\n\nclass TriggerCanvas {\n private canvas: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D;\n\n private canvasInitialized = false;\n\n constructor() {\n this.canvas = document.createElement(\"canvas\");\n const ctx = this.canvas.getContext(\"2d\", { willReadFrequently: true });\n if (!ctx) throw new Error(\"Canvas 2d context not ready\");\n this.ctx = ctx;\n this.ctx.fillStyle = \"transparent\";\n }\n\n initialize() {\n if (this.canvasInitialized) return;\n this.canvasInitialized = true;\n this.canvas.width = 1;\n this.canvas.height = 1;\n Object.assign(this.canvas.style, {\n position: \"fixed\",\n top: \"0px\",\n left: \"0px\",\n width: \"100%\",\n height: \"100%\",\n zIndex: \"100000\",\n });\n document.body.appendChild(this.canvas);\n }\n\n trigger() {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n }\n}\n\nexport class EFFramegen {\n time = 0;\n frameDurationMs = 0;\n audioBufferPromise?: Promise<AudioBuffer>;\n renderOptions?: VideoRenderOptions;\n frameBox = document.createElement(\"div\");\n BRIDGE: typeof window.FRAMEGEN_BRIDGE;\n triggerCanvas = new TriggerCanvas();\n verificationCanvas?: HTMLCanvasElement;\n verificationCtx?: CanvasRenderingContext2D;\n private logSequence = 0;\n\n // Frame sequence coordination\n public frameTasksInProgress = false;\n public currentFrameNumber = 0;\n\n // Accumulated per-phase seek timings for periodic reporting\n private seekTimingAccum: SeekForRenderTiming = {\n updateComplete1Ms: 0,\n updateComplete2Ms: 0,\n updateComplete3Ms: 0,\n textSegmentsMs: 0,\n renderFrameMs: 0,\n renderFrameQueryMs: 0,\n renderFramePrepareMs: 0,\n renderFrameDrawMs: 0,\n renderFrameAnimsMs: 0,\n frameTasksMs: 0,\n totalMs: 0,\n };\n private seekTimingCount = 0;\n\n trace(...args: any[]) {\n console.trace(\"[EF_FRAMEGEN]\", ...args);\n }\n\n async syncLog(...args: any[]): Promise<void> {\n if (!this.BRIDGE) {\n // Fallback to regular console.log if no bridge\n console.log(\"[EF_FRAMEGEN]\", ...args);\n return;\n }\n\n const sequence = ++this.logSequence;\n const message = args\n .map((arg) =>\n typeof arg === \"object\" ? JSON.stringify(arg) : String(arg),\n )\n .join(\" \");\n\n return new Promise<void>((resolve) => {\n // biome-ignore lint/style/noNonNullAssertion: We know BRIDGE is set due to the guard above\n this.BRIDGE!.syncLog(sequence, message, () => {\n resolve();\n });\n });\n }\n\n private initializeVerificationCanvas() {\n if (this.verificationCanvas) {\n return;\n }\n\n this.verificationCanvas = document.createElement(\"canvas\");\n const ctx = this.verificationCanvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Verification canvas 2d context not ready\");\n this.verificationCtx = ctx;\n\n // Size to match the workbench width, or fall back to renderOptions dimensions.\n // Without ef-workbench (e.g. API renders), the canvas was never sized or appended,\n // causing frame verification to fail on every frame.\n const workbench = document.querySelector(\"ef-workbench\") as HTMLElement;\n const canvasWidth = workbench\n ? workbench.clientWidth\n : (this.renderOptions?.encoderOptions.video.width ?? 0);\n\n if (canvasWidth > 0) {\n this.verificationCanvas.width = canvasWidth;\n this.verificationCanvas.height = 1;\n\n Object.assign(this.verificationCanvas.style, {\n position: \"fixed\",\n left: \"0px\",\n bottom: \"0px\",\n width: `${canvasWidth}px`,\n height: \"1px\",\n zIndex: \"99999\",\n });\n\n document.body.appendChild(this.verificationCanvas);\n }\n }\n\n private drawVerificationStrip(frameNumber: number) {\n this.initializeVerificationCanvas();\n\n if (!this.verificationCanvas || !this.verificationCtx) {\n return;\n }\n\n const width = this.verificationCanvas.width;\n const height = this.verificationCanvas.height;\n\n // Clear the strip\n this.verificationCtx.clearRect(0, 0, width, height);\n\n // Encode frame number into RGB (24-bit)\n // R=high byte, G=middle byte, B=low byte\n const red = Math.floor(frameNumber / (256 * 256)) % 256;\n const green = Math.floor(frameNumber / 256) % 256;\n const blue = frameNumber % 256;\n\n // Fill the entire strip with the encoded frame number\n this.verificationCtx.fillStyle = `rgb(${red}, ${green}, ${blue})`;\n this.verificationCtx.fillRect(0, 0, width, height);\n }\n\n constructor() {\n this.BRIDGE = window.FRAMEGEN_BRIDGE;\n if (this.BRIDGE) {\n this.connectToBridge();\n }\n }\n\n /**\n * Helper method to get the workbench and set its rendering state.\n * This ensures consistent state management across the framegen lifecycle.\n */\n private setWorkbenchRendering(isRendering: boolean) {\n const workbench = document.querySelector(\"ef-workbench\");\n if (workbench) {\n workbench.rendering = isRendering;\n }\n }\n\n connectToBridge() {\n const BRIDGE = this.BRIDGE;\n if (!BRIDGE) {\n throw new Error(\"No BRIDGE when attempting to connect to bridge\");\n }\n\n BRIDGE.onInitialize(async (renderOptions, traceContext, otelEndpoint) => {\n // Only enable tracing if explicitly requested in renderOptions\n if (renderOptions.enableTracing && otelEndpoint) {\n enableTracing();\n await setupBrowserTracing({\n otelEndpoint,\n serviceName: \"telecine-browser\",\n bridge: BRIDGE,\n useBatching: true, // Batch spans to reduce overhead during rendering\n });\n }\n\n const parentContext = extractParentContext(traceContext);\n\n await withSpan(\n \"browser.initialize\",\n {\n width: renderOptions.encoderOptions.video.width,\n height: renderOptions.encoderOptions.video.height,\n fps: renderOptions.encoderOptions.video.framerate,\n durationMs:\n renderOptions.encoderOptions.toMs -\n renderOptions.encoderOptions.fromMs,\n },\n parentContext,\n async () => {\n try {\n await this.initialize(renderOptions);\n } catch (error) {\n // If initialization fails, ensure rendering state is cleared\n this.setWorkbenchRendering(false);\n console.error(\n \"[EF_FRAMEGEN.connectToBridge] error initializing\",\n error,\n );\n throw error;\n }\n },\n );\n\n BRIDGE.initialized();\n });\n\n BRIDGE.onBeginFrame((frameNumber, isLast, traceContext) => {\n const parentContext = extractParentContext(traceContext);\n withSpanAndContext(\n \"browser.frame.render\",\n {\n frameNumber,\n isLast,\n },\n parentContext,\n async (span, _spanContext) => {\n // Store the span itself for child operations\n // This allows spans created in Lit Tasks to use it as their parent\n setCurrentFrameSpan(span);\n\n try {\n await this.beginFrame(frameNumber, isLast);\n } catch (error) {\n // If an error occurs during rendering, ensure rendering state is cleared\n this.setWorkbenchRendering(false);\n throw error;\n } finally {\n clearCurrentFrameSpan();\n }\n },\n ).catch((error) => {\n console.error(\"[EF_FRAMEGEN.beginFrame] error:\", error);\n // Ensure rendering state is cleared on error\n this.setWorkbenchRendering(false);\n clearCurrentFrameSpan();\n throw error;\n });\n });\n\n BRIDGE.onTriggerCanvas((traceContext) => {\n const parentContext = extractParentContext(traceContext);\n\n withSpan(\"browser.canvas.trigger\", {}, parentContext, async () => {\n this.triggerCanvas.trigger();\n }).catch((error) => {\n console.error(\"[EF_FRAMEGEN.triggerCanvas] error:\", error);\n });\n });\n }\n\n get showFrameBox() {\n return this.renderOptions?.showFrameBox ?? false;\n }\n\n async initialize(renderOptions: VideoRenderOptions) {\n this.renderOptions = renderOptions;\n\n // Workbench is optional - look for it but don't require it\n const workbench = document.querySelector(\"ef-workbench\");\n if (workbench) {\n this.setWorkbenchRendering(true);\n workbench.playing = false;\n }\n\n // Find timegroups either in workbench or directly in document\n const searchRoot = workbench || document.body;\n const timegroups = shallowGetTimegroups(searchRoot);\n const firstGroup = timegroups[0];\n if (!firstGroup) {\n throw new Error(\"No temporal elements found\");\n }\n const startingTimeMs = renderOptions.encoderOptions.fromMs;\n await firstGroup.waitForMediaDurations();\n\n // CRITICAL: Manually wire up temporal hierarchy since Lit Context fails with our connection order\n // When loading via loadURL(), elements connect depth-first (children before parents), causing\n // children to miss the context-request event since parents aren't listening yet.\n // See setupTemporalHierarchy.ts for detailed explanation.\n setupTemporalHierarchy(searchRoot, firstGroup);\n\n // Suppress autonomous re-renders (EFTemporal/EFTimegroup.updated) while\n // seekForRender is in progress — same protection applied to render clones.\n firstGroup.setAttribute(\"data-no-playback-controller\", \"\");\n\n // Use seekForRender for proper time seeking during rendering\n await firstGroup.seekForRender(startingTimeMs);\n\n this.frameDurationMs = 1000 / renderOptions.encoderOptions.video.framerate;\n\n this.time = startingTimeMs;\n if (this.showFrameBox) {\n Object.assign(this.frameBox.style, {\n width: \"200px\",\n height: \"100px\",\n font: \"10px Arial\",\n backgroundColor: \"white\",\n position: \"absolute\",\n top: \"0px\",\n right: \"0px\",\n zIndex: \"100000\",\n });\n document.body.prepend(this.frameBox);\n }\n\n this.triggerCanvas.initialize();\n\n // These times are aligned to the audio frame boundaries\n // And they include padding if any.\n this.audioBufferPromise = firstGroup.renderAudio(\n renderOptions.encoderOptions.alignedFromUs / 1000,\n renderOptions.encoderOptions.alignedToUs / 1000,\n );\n // Suppress unhandled rejection while the promise sits in storage before being awaited.\n this.audioBufferPromise.catch(() => {});\n }\n\n async beginFrame(frameNumber: number, isLast: boolean) {\n if (this.renderOptions === undefined) {\n throw new Error(\"No renderOptions\");\n }\n const workbench = document.querySelector(\"ef-workbench\");\n if (workbench) {\n this.setWorkbenchRendering(true);\n }\n const searchRoot = workbench || document.body;\n const timegroups = shallowGetTimegroups(searchRoot);\n const firstGroup = timegroups[0];\n if (!firstGroup) {\n throw new Error(\"No temporal elements found\");\n }\n\n // Calculate base frame time using normal progression\n const frameTime =\n this.renderOptions.encoderOptions.fromMs +\n frameNumber * this.frameDurationMs;\n const frameTimeMs = Number(Number(frameTime).toFixed(5));\n\n // Use seekForRender for proper time seeking during rendering\n const seekTiming = await firstGroup.seekForRender(frameTimeMs);\n this.seekTimingAccum.updateComplete1Ms += seekTiming.updateComplete1Ms;\n this.seekTimingAccum.updateComplete2Ms += seekTiming.updateComplete2Ms;\n this.seekTimingAccum.updateComplete3Ms += seekTiming.updateComplete3Ms;\n this.seekTimingAccum.textSegmentsMs += seekTiming.textSegmentsMs;\n this.seekTimingAccum.renderFrameMs += seekTiming.renderFrameMs;\n this.seekTimingAccum.renderFrameQueryMs += seekTiming.renderFrameQueryMs;\n this.seekTimingAccum.renderFramePrepareMs += seekTiming.renderFramePrepareMs;\n this.seekTimingAccum.renderFrameDrawMs += seekTiming.renderFrameDrawMs;\n this.seekTimingAccum.renderFrameAnimsMs += seekTiming.renderFrameAnimsMs;\n this.seekTimingAccum.frameTasksMs += seekTiming.frameTasksMs;\n this.seekTimingAccum.totalMs += seekTiming.totalMs;\n this.seekTimingCount++;\n\n if (this.seekTimingCount % 30 === 0) {\n const n = this.seekTimingCount;\n console.log(\n \"[EF_FRAMEGEN] seekForRender phase avg (ms) over\",\n n,\n \"frames:\",\n JSON.stringify({\n uc1: (this.seekTimingAccum.updateComplete1Ms / n).toFixed(2),\n uc2: (this.seekTimingAccum.updateComplete2Ms / n).toFixed(2),\n uc3: (this.seekTimingAccum.updateComplete3Ms / n).toFixed(2),\n textSegments: (this.seekTimingAccum.textSegmentsMs / n).toFixed(2),\n renderFrame: (this.seekTimingAccum.renderFrameMs / n).toFixed(2),\n rf_query: (this.seekTimingAccum.renderFrameQueryMs / n).toFixed(2),\n rf_prepare: (this.seekTimingAccum.renderFramePrepareMs / n).toFixed(2),\n rf_draw: (this.seekTimingAccum.renderFrameDrawMs / n).toFixed(2),\n rf_anims: (this.seekTimingAccum.renderFrameAnimsMs / n).toFixed(2),\n frameTasks: (this.seekTimingAccum.frameTasksMs / n).toFixed(2),\n total: (this.seekTimingAccum.totalMs / n).toFixed(2),\n }),\n );\n }\n\n if (this.showFrameBox) {\n this.frameBox.innerHTML = `\n <div>🖼️ Frame: ${frameNumber}</div>\n <div>🕛 Segment: ${this.time.toFixed(4)}</div>\n <div>🕛 Frame: ${firstGroup.currentTimeMs.toFixed(4)}</div>\n <div> from-to: ${this.renderOptions.encoderOptions.fromMs.toFixed(4)} - ${this.renderOptions.encoderOptions.toMs.toFixed(4)}</div>\n `;\n }\n\n // Draw verification pixel strip for frame verification\n this.drawVerificationStrip(frameNumber);\n\n if (isLast && this.audioBufferPromise) {\n // Currently we emit the audio in one belch at the end of the render.\n // This is not ideal, but it's the simplest thing that could possibly work.\n // We could either emit it slices, or in parallel with the video.\n // But in any case, it's fine for now.\n const renderedAudio = await this.audioBufferPromise;\n\n const channelCount = renderedAudio.numberOfChannels;\n\n const interleavedSamples = new Float32Array(\n channelCount * renderedAudio.length,\n );\n\n for (let i = 0; i < renderedAudio.length; i++) {\n for (let j = 0; j < channelCount; j++) {\n interleavedSamples.set(\n renderedAudio.getChannelData(j).slice(i, i + 1),\n i * channelCount + j,\n );\n }\n }\n\n if (this.BRIDGE) {\n this.BRIDGE.frameReady(frameNumber, interleavedSamples.buffer);\n } else {\n const fileReader = new FileReader();\n fileReader.readAsDataURL(new Blob([interleavedSamples.buffer]));\n await new Promise((resolve, reject) => {\n fileReader.onload = resolve;\n fileReader.onerror = reject;\n });\n return fileReader.result;\n }\n\n // Rendering is complete after the last frame\n this.setWorkbenchRendering(false);\n } else {\n if (this.BRIDGE) {\n this.BRIDGE.frameReady(frameNumber, new ArrayBuffer(0));\n } else {\n const fileReader = new FileReader();\n fileReader.readAsDataURL(new Blob([]));\n await new Promise((resolve, reject) => {\n fileReader.onload = resolve;\n fileReader.onerror = reject;\n });\n return fileReader.result;\n }\n }\n }\n}\n\nif (typeof window !== \"undefined\") {\n window.EF_FRAMEGEN = new EFFramegen();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EF_FRAMEGEN.js","names":[],"sources":["../src/EF_FRAMEGEN.ts"],"sourcesContent":["import type { VideoRenderOptions } from \"@editframe/assets\";\n\nimport {\n shallowGetTimegroups,\n type SeekForRenderTiming,\n} from \"./elements/EFTimegroup.js\";\nimport { setupTemporalHierarchy } from \"./elements/setupTemporalHierarchy.js\";\n\nimport { setupBrowserTracing } from \"./otel/setupBrowserTracing.js\";\nimport {\n clearCurrentFrameSpan,\n enableTracing,\n extractParentContext,\n setCurrentFrameSpan,\n type TraceContext,\n withSpan,\n withSpanAndContext,\n} from \"./otel/tracingHelpers.js\";\n\ninterface Bridge {\n onInitialize: (\n callback: (\n renderOptions: VideoRenderOptions,\n traceContext?: TraceContext,\n otelEndpoint?: string,\n ) => void,\n ) => void;\n\n initialized(): void;\n\n onBeginFrame(\n callback: (\n frameNumber: number,\n isLast: boolean,\n traceContext?: TraceContext,\n ) => void,\n ): void;\n\n onTriggerCanvas(callback: (traceContext?: TraceContext) => void): void;\n\n frameReady(frameNumber: number, audioSamples: ArrayBuffer): void;\n\n error(error: Error): void;\n\n syncLog(sequence: number, message: string, callback: () => void): void;\n\n exportSpans?: (endpoint: string, payload: string) => void;\n}\n\ndeclare global {\n interface Window {\n EF_FRAMEGEN?: EFFramegen;\n FRAMEGEN_BRIDGE?: Bridge;\n FRAMEGEN_BINDING?: any;\n FRAMEGEN_BINDING_error?: (error: Error) => void;\n EF_RENDERING?: () => boolean;\n }\n}\n\nclass TriggerCanvas {\n private canvas: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D;\n\n private canvasInitialized = false;\n\n constructor() {\n this.canvas = document.createElement(\"canvas\");\n const ctx = this.canvas.getContext(\"2d\", { willReadFrequently: true });\n if (!ctx) throw new Error(\"Canvas 2d context not ready\");\n this.ctx = ctx;\n this.ctx.fillStyle = \"transparent\";\n }\n\n initialize() {\n if (this.canvasInitialized) return;\n this.canvasInitialized = true;\n this.canvas.width = 1;\n this.canvas.height = 1;\n Object.assign(this.canvas.style, {\n position: \"fixed\",\n top: \"0px\",\n left: \"0px\",\n width: \"100%\",\n height: \"100%\",\n zIndex: \"100000\",\n });\n document.body.appendChild(this.canvas);\n }\n\n trigger() {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n }\n}\n\nexport class EFFramegen {\n time = 0;\n frameDurationMs = 0;\n audioBufferPromise?: Promise<AudioBuffer>;\n renderOptions?: VideoRenderOptions;\n frameBox = document.createElement(\"div\");\n BRIDGE: typeof window.FRAMEGEN_BRIDGE;\n triggerCanvas = new TriggerCanvas();\n verificationCanvas?: HTMLCanvasElement;\n verificationCtx?: CanvasRenderingContext2D;\n private logSequence = 0;\n\n // Frame sequence coordination\n public frameTasksInProgress = false;\n public currentFrameNumber = 0;\n\n // Per-phase timing accumulators (reset every 30 frames)\n private timingFrameCount = 0;\n private timingAccum: SeekForRenderTiming = {\n updateComplete1Ms: 0,\n updateComplete2Ms: 0,\n updateComplete3Ms: 0,\n textSegmentsMs: 0,\n renderFrameMs: 0,\n renderFrameQueryMs: 0,\n renderFramePrepareMs: 0,\n renderFrameDrawMs: 0,\n renderFrameAnimsMs: 0,\n frameTasksMs: 0,\n totalMs: 0,\n };\n\n trace(...args: any[]) {\n console.trace(\"[EF_FRAMEGEN]\", ...args);\n }\n\n async syncLog(...args: any[]): Promise<void> {\n if (!this.BRIDGE) {\n // Fallback to regular console.log if no bridge\n console.log(\"[EF_FRAMEGEN]\", ...args);\n return;\n }\n\n const sequence = ++this.logSequence;\n const message = args\n .map((arg) =>\n typeof arg === \"object\" ? JSON.stringify(arg) : String(arg),\n )\n .join(\" \");\n\n return new Promise<void>((resolve) => {\n // biome-ignore lint/style/noNonNullAssertion: We know BRIDGE is set due to the guard above\n this.BRIDGE!.syncLog(sequence, message, () => {\n resolve();\n });\n });\n }\n\n private initializeVerificationCanvas() {\n if (this.verificationCanvas) {\n return;\n }\n\n this.verificationCanvas = document.createElement(\"canvas\");\n const ctx = this.verificationCanvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Verification canvas 2d context not ready\");\n this.verificationCtx = ctx;\n\n // Size to match the workbench width, or fall back to renderOptions dimensions.\n // Without ef-workbench (e.g. API renders), the canvas was never sized or appended,\n // causing frame verification to fail on every frame.\n const workbench = document.querySelector(\"ef-workbench\") as HTMLElement;\n const canvasWidth = workbench\n ? workbench.clientWidth\n : (this.renderOptions?.encoderOptions.video.width ?? 0);\n\n if (canvasWidth > 0) {\n this.verificationCanvas.width = canvasWidth;\n this.verificationCanvas.height = 1;\n\n Object.assign(this.verificationCanvas.style, {\n position: \"fixed\",\n left: \"0px\",\n bottom: \"0px\",\n width: `${canvasWidth}px`,\n height: \"1px\",\n zIndex: \"99999\",\n });\n\n document.body.appendChild(this.verificationCanvas);\n }\n }\n\n private drawVerificationStrip(frameNumber: number) {\n this.initializeVerificationCanvas();\n\n if (!this.verificationCanvas || !this.verificationCtx) {\n return;\n }\n\n const width = this.verificationCanvas.width;\n const height = this.verificationCanvas.height;\n\n // Clear the strip\n this.verificationCtx.clearRect(0, 0, width, height);\n\n // Encode frame number into RGB (24-bit)\n // R=high byte, G=middle byte, B=low byte\n const red = Math.floor(frameNumber / (256 * 256)) % 256;\n const green = Math.floor(frameNumber / 256) % 256;\n const blue = frameNumber % 256;\n\n // Fill the entire strip with the encoded frame number\n this.verificationCtx.fillStyle = `rgb(${red}, ${green}, ${blue})`;\n this.verificationCtx.fillRect(0, 0, width, height);\n }\n\n constructor() {\n this.BRIDGE = window.FRAMEGEN_BRIDGE;\n if (this.BRIDGE) {\n this.connectToBridge();\n }\n }\n\n /**\n * Helper method to get the workbench and set its rendering state.\n * This ensures consistent state management across the framegen lifecycle.\n */\n private setWorkbenchRendering(isRendering: boolean) {\n const workbench = document.querySelector(\"ef-workbench\");\n if (workbench) {\n workbench.rendering = isRendering;\n }\n }\n\n connectToBridge() {\n const BRIDGE = this.BRIDGE;\n if (!BRIDGE) {\n throw new Error(\"No BRIDGE when attempting to connect to bridge\");\n }\n\n BRIDGE.onInitialize(async (renderOptions, traceContext, otelEndpoint) => {\n // Only enable tracing if explicitly requested in renderOptions\n if (renderOptions.enableTracing && otelEndpoint) {\n enableTracing();\n await setupBrowserTracing({\n otelEndpoint,\n serviceName: \"telecine-browser\",\n bridge: BRIDGE,\n useBatching: true, // Batch spans to reduce overhead during rendering\n });\n }\n\n const parentContext = extractParentContext(traceContext);\n\n await withSpan(\n \"browser.initialize\",\n {\n width: renderOptions.encoderOptions.video.width,\n height: renderOptions.encoderOptions.video.height,\n fps: renderOptions.encoderOptions.video.framerate,\n durationMs:\n renderOptions.encoderOptions.toMs -\n renderOptions.encoderOptions.fromMs,\n },\n parentContext,\n async () => {\n try {\n await this.initialize(renderOptions);\n } catch (error) {\n // If initialization fails, ensure rendering state is cleared\n this.setWorkbenchRendering(false);\n console.error(\n \"[EF_FRAMEGEN.connectToBridge] error initializing\",\n error,\n );\n throw error;\n }\n },\n );\n\n BRIDGE.initialized();\n });\n\n BRIDGE.onBeginFrame((frameNumber, isLast, traceContext) => {\n const parentContext = extractParentContext(traceContext);\n withSpanAndContext(\n \"browser.frame.render\",\n {\n frameNumber,\n isLast,\n },\n parentContext,\n async (span, _spanContext) => {\n // Store the span itself for child operations\n // This allows spans created in Lit Tasks to use it as their parent\n setCurrentFrameSpan(span);\n\n try {\n await this.beginFrame(frameNumber, isLast);\n } catch (error) {\n // If an error occurs during rendering, ensure rendering state is cleared\n this.setWorkbenchRendering(false);\n throw error;\n } finally {\n clearCurrentFrameSpan();\n }\n },\n ).catch((error) => {\n console.error(\"[EF_FRAMEGEN.beginFrame] error:\", error);\n // Ensure rendering state is cleared on error\n this.setWorkbenchRendering(false);\n clearCurrentFrameSpan();\n throw error;\n });\n });\n\n BRIDGE.onTriggerCanvas((traceContext) => {\n const parentContext = extractParentContext(traceContext);\n\n withSpan(\"browser.canvas.trigger\", {}, parentContext, async () => {\n this.triggerCanvas.trigger();\n }).catch((error) => {\n console.error(\"[EF_FRAMEGEN.triggerCanvas] error:\", error);\n });\n });\n }\n\n get showFrameBox() {\n return this.renderOptions?.showFrameBox ?? false;\n }\n\n async initialize(renderOptions: VideoRenderOptions) {\n this.renderOptions = renderOptions;\n\n // Workbench is optional - look for it but don't require it\n const workbench = document.querySelector(\"ef-workbench\");\n if (workbench) {\n this.setWorkbenchRendering(true);\n workbench.playing = false;\n }\n\n // Find timegroups either in workbench or directly in document\n const searchRoot = workbench || document.body;\n const timegroups = shallowGetTimegroups(searchRoot);\n const firstGroup = timegroups[0];\n if (!firstGroup) {\n throw new Error(\"No temporal elements found\");\n }\n const startingTimeMs = renderOptions.encoderOptions.fromMs;\n await firstGroup.waitForMediaDurations();\n\n // CRITICAL: Manually wire up temporal hierarchy since Lit Context fails with our connection order\n // When loading via loadURL(), elements connect depth-first (children before parents), causing\n // children to miss the context-request event since parents aren't listening yet.\n // See setupTemporalHierarchy.ts for detailed explanation.\n setupTemporalHierarchy(searchRoot, firstGroup);\n\n // Suppress autonomous re-renders (EFTemporal/EFTimegroup.updated) while\n // seekForRender is in progress — same protection applied to render clones.\n firstGroup.setAttribute(\"data-no-playback-controller\", \"\");\n\n // Use seekForRender for proper time seeking during rendering\n await firstGroup.seekForRender(startingTimeMs);\n\n this.frameDurationMs = 1000 / renderOptions.encoderOptions.video.framerate;\n\n this.time = startingTimeMs;\n if (this.showFrameBox) {\n Object.assign(this.frameBox.style, {\n width: \"200px\",\n height: \"100px\",\n font: \"10px Arial\",\n backgroundColor: \"white\",\n position: \"absolute\",\n top: \"0px\",\n right: \"0px\",\n zIndex: \"100000\",\n });\n document.body.prepend(this.frameBox);\n }\n\n this.triggerCanvas.initialize();\n\n // These times are aligned to the audio frame boundaries\n // And they include padding if any.\n this.audioBufferPromise = firstGroup.renderAudio(\n renderOptions.encoderOptions.alignedFromUs / 1000,\n renderOptions.encoderOptions.alignedToUs / 1000,\n );\n // Suppress unhandled rejection while the promise sits in storage before being awaited.\n this.audioBufferPromise.catch(() => {});\n }\n\n async beginFrame(frameNumber: number, isLast: boolean) {\n if (this.renderOptions === undefined) {\n throw new Error(\"No renderOptions\");\n }\n const workbench = document.querySelector(\"ef-workbench\");\n if (workbench) {\n this.setWorkbenchRendering(true);\n }\n const searchRoot = workbench || document.body;\n const timegroups = shallowGetTimegroups(searchRoot);\n const firstGroup = timegroups[0];\n if (!firstGroup) {\n throw new Error(\"No temporal elements found\");\n }\n\n // Calculate base frame time using normal progression\n const frameTime =\n this.renderOptions.encoderOptions.fromMs +\n frameNumber * this.frameDurationMs;\n const frameTimeMs = Number(Number(frameTime).toFixed(5));\n\n // Use seekForRender for proper time seeking during rendering\n const timing = await firstGroup.seekForRender(frameTimeMs);\n this.timingFrameCount++;\n for (const key of Object.keys(\n this.timingAccum,\n ) as (keyof SeekForRenderTiming)[]) {\n this.timingAccum[key] += timing[key];\n }\n if (this.timingFrameCount >= 30) {\n const n = this.timingFrameCount;\n console.log(\n `[EF_FRAMEGEN] seekForRender phase avg (${n} frames):`,\n `total=${(this.timingAccum.totalMs / n).toFixed(1)}ms`,\n `uc1=${(this.timingAccum.updateComplete1Ms / n).toFixed(1)}ms`,\n `uc2=${(this.timingAccum.updateComplete2Ms / n).toFixed(1)}ms`,\n `uc3=${(this.timingAccum.updateComplete3Ms / n).toFixed(1)}ms`,\n `text=${(this.timingAccum.textSegmentsMs / n).toFixed(1)}ms`,\n `renderFrame=${(this.timingAccum.renderFrameMs / n).toFixed(1)}ms`,\n `rf.query=${(this.timingAccum.renderFrameQueryMs / n).toFixed(1)}ms`,\n `rf.prepare=${(this.timingAccum.renderFramePrepareMs / n).toFixed(1)}ms`,\n `rf.draw=${(this.timingAccum.renderFrameDrawMs / n).toFixed(1)}ms`,\n `rf.anims=${(this.timingAccum.renderFrameAnimsMs / n).toFixed(1)}ms`,\n `frameTasks=${(this.timingAccum.frameTasksMs / n).toFixed(1)}ms`,\n );\n this.timingFrameCount = 0;\n for (const key of Object.keys(\n this.timingAccum,\n ) as (keyof SeekForRenderTiming)[]) {\n this.timingAccum[key] = 0;\n }\n }\n if (this.showFrameBox) {\n this.frameBox.innerHTML = `\n <div>🖼️ Frame: ${frameNumber}</div>\n <div>🕛 Segment: ${this.time.toFixed(4)}</div>\n <div>🕛 Frame: ${firstGroup.currentTimeMs.toFixed(4)}</div>\n <div> from-to: ${this.renderOptions.encoderOptions.fromMs.toFixed(4)} - ${this.renderOptions.encoderOptions.toMs.toFixed(4)}</div>\n `;\n }\n\n // Draw verification pixel strip for frame verification\n this.drawVerificationStrip(frameNumber);\n\n if (isLast && this.audioBufferPromise) {\n // Currently we emit the audio in one belch at the end of the render.\n // This is not ideal, but it's the simplest thing that could possibly work.\n // We could either emit it slices, or in parallel with the video.\n // But in any case, it's fine for now.\n const renderedAudio = await this.audioBufferPromise;\n\n const channelCount = renderedAudio.numberOfChannels;\n\n const interleavedSamples = new Float32Array(\n channelCount * renderedAudio.length,\n );\n\n for (let i = 0; i < renderedAudio.length; i++) {\n for (let j = 0; j < channelCount; j++) {\n interleavedSamples.set(\n renderedAudio.getChannelData(j).slice(i, i + 1),\n i * channelCount + j,\n );\n }\n }\n\n if (this.BRIDGE) {\n this.BRIDGE.frameReady(frameNumber, interleavedSamples.buffer);\n } else {\n const fileReader = new FileReader();\n fileReader.readAsDataURL(new Blob([interleavedSamples.buffer]));\n await new Promise((resolve, reject) => {\n fileReader.onload = resolve;\n fileReader.onerror = reject;\n });\n return fileReader.result;\n }\n\n // Rendering is complete after the last frame\n this.setWorkbenchRendering(false);\n } else {\n if (this.BRIDGE) {\n this.BRIDGE.frameReady(frameNumber, new ArrayBuffer(0));\n } else {\n const fileReader = new FileReader();\n fileReader.readAsDataURL(new Blob([]));\n await new Promise((resolve, reject) => {\n fileReader.onload = resolve;\n fileReader.onerror = reject;\n });\n return fileReader.result;\n }\n }\n }\n}\n\nif (typeof window !== \"undefined\") {\n window.EF_FRAMEGEN = new EFFramegen();\n}\n"],"mappings":"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"}
@@ -5,9 +5,9 @@ import { PanZoomTransform } from "../elements/EFPanZoom.js";
5
5
  import "./overlays/SelectionOverlay.js";
6
6
  import "../gui/EFOverlayLayer.js";
7
7
  import "../gui/EFTransformHandles.js";
8
- import * as lit29 from "lit";
8
+ import * as lit30 from "lit";
9
9
  import { LitElement } from "lit";
10
- import * as lit_html27 from "lit-html";
10
+ import * as lit_html28 from "lit-html";
11
11
 
12
12
  //#region src/canvas/EFCanvas.d.ts
13
13
  declare const EFCanvas_base: typeof LitElement;
@@ -87,7 +87,7 @@ declare const EFCanvas_base: typeof LitElement;
87
87
  * Manages existing elements (EF* elements, divs, etc.) and provides selection functionality.
88
88
  */
89
89
  declare class EFCanvas extends EFCanvas_base {
90
- static styles: lit29.CSSResult[];
90
+ static styles: lit30.CSSResult[];
91
91
  panZoomTransform?: PanZoomTransform;
92
92
  elementIdAttribute: string;
93
93
  enableTransformHandles: boolean;
@@ -309,7 +309,7 @@ declare class EFCanvas extends EFCanvas_base {
309
309
  * Cleanup transform handles.
310
310
  */
311
311
  private cleanupTransformHandles;
312
- render(): lit_html27.TemplateResult<1>;
312
+ render(): lit_html28.TemplateResult<1>;
313
313
  }
314
314
  declare global {
315
315
  interface HTMLElementTagNameMap {
@@ -1,6 +1,6 @@
1
- import * as lit30 from "lit";
1
+ import * as lit31 from "lit";
2
2
  import { LitElement } from "lit";
3
- import * as lit_html28 from "lit-html";
3
+ import * as lit_html29 from "lit-html";
4
4
 
5
5
  //#region src/canvas/EFCanvasItem.d.ts
6
6
 
@@ -28,7 +28,7 @@ import * as lit_html28 from "lit-html";
28
28
  * ```
29
29
  */
30
30
  declare class EFCanvasItem extends LitElement {
31
- static styles: lit30.CSSResult;
31
+ static styles: lit31.CSSResult;
32
32
  id: string;
33
33
  private canvas;
34
34
  private api;
@@ -43,7 +43,7 @@ declare class EFCanvasItem extends LitElement {
43
43
  * Unregister this element from the canvas.
44
44
  */
45
45
  private unregister;
46
- render(): lit_html28.TemplateResult<1>;
46
+ render(): lit_html29.TemplateResult<1>;
47
47
  }
48
48
  declare global {
49
49
  interface HTMLElementTagNameMap {
@@ -1,6 +1,6 @@
1
- import * as lit28 from "lit";
1
+ import * as lit29 from "lit";
2
2
  import { LitElement } from "lit";
3
- import * as lit_html26 from "lit-html";
3
+ import * as lit_html27 from "lit-html";
4
4
 
5
5
  //#region src/elements/EFPanZoom.d.ts
6
6
  interface PanZoomTransform {
@@ -9,7 +9,7 @@ interface PanZoomTransform {
9
9
  scale: number;
10
10
  }
11
11
  declare class EFPanZoom extends LitElement {
12
- static styles: lit28.CSSResult[];
12
+ static styles: lit29.CSSResult[];
13
13
  x: number;
14
14
  y: number;
15
15
  scale: number;
@@ -89,7 +89,7 @@ declare class EFPanZoom extends LitElement {
89
89
  * @param padding - Padding factor (0-1), e.g., 0.1 = 10% padding on each side. Default: 0.05
90
90
  */
91
91
  fitToContent(padding?: number): void;
92
- render(): lit_html26.TemplateResult<1>;
92
+ render(): lit_html27.TemplateResult<1>;
93
93
  }
94
94
  //#endregion
95
95
  export { EFPanZoom, PanZoomTransform };
@@ -1,18 +1,18 @@
1
1
  import { FrameRenderable, FrameState } from "../preview/FrameController.js";
2
2
  import { ContextMixinInterface } from "../gui/ContextMixin.js";
3
- import * as lit27 from "lit";
3
+ import * as lit28 from "lit";
4
4
  import { LitElement } from "lit";
5
- import * as lit_html25 from "lit-html";
5
+ import * as lit_html26 from "lit-html";
6
6
  import * as lit_html_directives_ref3 from "lit-html/directives/ref";
7
7
 
8
8
  //#region src/elements/EFSurface.d.ts
9
9
  declare class EFSurface extends LitElement implements FrameRenderable {
10
10
  #private;
11
- static styles: lit27.CSSResult[];
11
+ static styles: lit28.CSSResult[];
12
12
  canvasRef: lit_html_directives_ref3.Ref<HTMLCanvasElement>;
13
13
  targetElement: ContextMixinInterface | null;
14
14
  target: string;
15
- render(): lit_html25.TemplateResult<1>;
15
+ render(): lit_html26.TemplateResult<1>;
16
16
  get rootTimegroup(): any;
17
17
  get currentTimeMs(): number;
18
18
  get durationMs(): number;
@@ -45,6 +45,18 @@ interface RenderCloneResult {
45
45
  * - GPU operations (WebGL context creation, shader compilation) may take up to ~1s
46
46
  */
47
47
  type TimegroupInitializer = (timegroup: EFTimegroup) => void;
48
+ /**
49
+ * The four timegroup modes define how duration is calculated:
50
+ * - "fit": Inherits duration from parent timegroup
51
+ * - "fixed": Uses explicit duration attribute
52
+ * - "sequence": Sum of child durations minus overlaps
53
+ * - "contain": Maximum of child durations
54
+ */
55
+ type TimeMode = "fit" | "fixed" | "sequence" | "contain";
56
+ /**
57
+ * Per-phase timing data returned by seekForRender().
58
+ * All values are in milliseconds.
59
+ */
48
60
  interface SeekForRenderTiming {
49
61
  updateComplete1Ms: number;
50
62
  updateComplete2Ms: number;
@@ -58,14 +70,6 @@ interface SeekForRenderTiming {
58
70
  frameTasksMs: number;
59
71
  totalMs: number;
60
72
  }
61
- /**
62
- * The four timegroup modes define how duration is calculated:
63
- * - "fit": Inherits duration from parent timegroup
64
- * - "fixed": Uses explicit duration attribute
65
- * - "sequence": Sum of child durations minus overlaps
66
- * - "contain": Maximum of child durations
67
- */
68
- type TimeMode = "fit" | "fixed" | "sequence" | "contain";
69
73
  declare const EFTimegroup_base: (new (...args: any[]) => TemporalMixinInterface) & typeof LitElement;
70
74
  declare class EFTimegroup extends EFTimegroup_base implements FrameRenderable {
71
75
  #private;
@@ -575,14 +575,18 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
575
575
  if (this.playbackController) this.playbackController.currentTime = newTime;
576
576
  this._setLocalTimeMs(timeMs);
577
577
  this.requestUpdate("currentTime");
578
- await this.updateComplete;
579
578
  const t1 = performance.now();
579
+ await this.updateComplete;
580
+ const updateComplete1Ms = performance.now() - t1;
580
581
  const allLitElements = this.#getAllLitElementDescendants();
581
- await Promise.all(allLitElements.map((el) => el.updateComplete));
582
582
  const t2 = performance.now();
583
583
  await Promise.all(allLitElements.map((el) => el.updateComplete));
584
+ const updateComplete2Ms = performance.now() - t2;
584
585
  const t3 = performance.now();
586
+ await Promise.all(allLitElements.map((el) => el.updateComplete));
587
+ const updateComplete3Ms = performance.now() - t3;
585
588
  const textElements = allLitElements.filter((el) => el.tagName === "EF-TEXT");
589
+ const t4 = performance.now();
586
590
  if (textElements.length > 0) {
587
591
  await Promise.all(textElements.map((el) => {
588
592
  if ("whenSegmentsReady" in el && typeof el.whenSegmentsReady === "function") return el.whenSegmentsReady();
@@ -590,29 +594,32 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
590
594
  }));
591
595
  this.offsetHeight;
592
596
  }
593
- const t4 = performance.now();
594
- const rfTiming = await this.#frameController.renderFrame(timeMs, {
597
+ const textSegmentsMs = performance.now() - t4;
598
+ const t5 = performance.now();
599
+ const frameControllerTiming = await this.#frameController.renderFrame(timeMs, {
595
600
  waitForLitUpdate: false,
596
601
  onAnimationsUpdate: (root) => {
597
602
  updateAnimations(root);
598
603
  root.offsetWidth;
599
604
  }
600
605
  });
601
- const t5 = performance.now();
602
- await this.#executeCustomFrameTasks();
606
+ const renderFrameMs = performance.now() - t5;
603
607
  const t6 = performance.now();
608
+ await this.#executeCustomFrameTasks();
609
+ const frameTasksMs = performance.now() - t6;
610
+ const totalMs = performance.now() - t0;
604
611
  return {
605
- updateComplete1Ms: t1 - t0,
606
- updateComplete2Ms: t2 - t1,
607
- updateComplete3Ms: t3 - t2,
608
- textSegmentsMs: t4 - t3,
609
- renderFrameMs: t5 - t4,
610
- renderFrameQueryMs: rfTiming.queryMs,
611
- renderFramePrepareMs: rfTiming.prepareMs,
612
- renderFrameDrawMs: rfTiming.renderMs,
613
- renderFrameAnimsMs: rfTiming.animsMs,
614
- frameTasksMs: t6 - t5,
615
- totalMs: t6 - t0
612
+ updateComplete1Ms,
613
+ updateComplete2Ms,
614
+ updateComplete3Ms,
615
+ textSegmentsMs,
616
+ renderFrameMs,
617
+ renderFrameQueryMs: frameControllerTiming?.queryMs ?? 0,
618
+ renderFramePrepareMs: frameControllerTiming?.prepareMs ?? 0,
619
+ renderFrameDrawMs: frameControllerTiming?.renderMs ?? 0,
620
+ renderFrameAnimsMs: frameControllerTiming?.animsMs ?? 0,
621
+ frameTasksMs,
622
+ totalMs
616
623
  };
617
624
  }
618
625
  /**