@@ -109,6 +109,7 @@ import {
109109 createCapturePlan ,
110110 replanAfterFailure ,
111111 type CapturePlan ,
112+ type SdrDiskCapturePlan ,
112113 type CaptureRouting ,
113114} from "./render/capturePlan.js" ;
114115import { normalizeErrorMessage } from "../utils/errorMessage.js" ;
@@ -3144,34 +3145,92 @@ async function executeRenderPipeline(input: {
31443145 if ( capturePlan . kind !== "sdr_disk" ) {
31453146 throw new Error ( `Disk capture requires sdr_disk plan; got ${ capturePlan . kind } ` ) ;
31463147 }
3147- const diskPlan = capturePlan ;
31483148 // ── Disk-based capture (original flow) ────────────────────────────
31493149 resetCaptureAttemptProgress ( job ) ;
31503150 const captureFrameStart = Date . now ( ) ;
3151- const captureRes = await observeRenderStage (
3152- observability ,
3153- "capture_disk" ,
3154- captureStageObservationData ( { needsAlpha : diskPlan . needsAlpha } ) ,
3155- ( ) =>
3156- runCaptureStage ( {
3157- fileServer : activeFileServer ,
3158- workDir,
3159- framesDir,
3160- job,
3161- totalFrames,
3162- cfg,
3163- plan : diskPlan ,
3164- log,
3165- probeSession,
3166- captureAttempts,
3167- dedupPerfs,
3168- buildCaptureOptions,
3169- createRenderVideoFrameInjector,
3170- abortSignal : executionSignal ,
3171- assertNotAborted,
3172- onProgress,
3173- } ) ,
3174- ) ;
3151+ const invokeDiskCapture = ( diskPlan : SdrDiskCapturePlan ) =>
3152+ observeRenderStage (
3153+ observability ,
3154+ "capture_disk" ,
3155+ captureStageObservationData ( { needsAlpha : diskPlan . needsAlpha } ) ,
3156+ ( ) =>
3157+ runCaptureStage ( {
3158+ fileServer : activeFileServer ,
3159+ workDir,
3160+ framesDir,
3161+ job,
3162+ totalFrames,
3163+ cfg,
3164+ plan : diskPlan ,
3165+ log,
3166+ probeSession,
3167+ captureAttempts,
3168+ dedupPerfs,
3169+ buildCaptureOptions,
3170+ createRenderVideoFrameInjector,
3171+ abortSignal : executionSignal ,
3172+ assertNotAborted,
3173+ onProgress,
3174+ } ) ,
3175+ ) ;
3176+ let captureRes ;
3177+ try {
3178+ captureRes = await invokeDiskCapture ( capturePlan ) ;
3179+ } catch ( err ) {
3180+ // Disk-path drawElement self-verification tripped (a parallel disk
3181+ // worker's sampled frame diverged from its pre-injection ground
3182+ // truth — reachable only under the explicit fast-capture opt-in).
3183+ // Same recovery contract as the streaming drain: re-render on the
3184+ // screenshot baseline. Anything else keeps its existing semantics.
3185+ if (
3186+ ! isDrawElementVerificationError ( err ) ||
3187+ err instanceof RenderCancelledError ||
3188+ executionSignal ?. aborted === true
3189+ ) {
3190+ throw err ;
3191+ }
3192+ const verifyDetails = getDrawElementVerificationDetails ( err ) ;
3193+ deSelfVerifyFallback = true ;
3194+ deFallbackReason = verifyDetails ?. kind ?? "psnr" ;
3195+ deFallbackFailedDb = roundDb ( verifyDetails ?. failedDb ) ;
3196+ deFallbackFrameIndex = verifyDetails ?. frameIndex ;
3197+ deFallbackThresholdDb = roundDb ( verifyDetails ?. verifyThresholdDb ) ;
3198+ log . warn (
3199+ "[Render] drawElement self-verification failed on the parallel disk path; " +
3200+ "re-rendering via screenshot" ,
3201+ { error : err instanceof Error ? err . message : String ( err ) } ,
3202+ ) ;
3203+ observability . checkpoint (
3204+ "capture_disk" ,
3205+ "drawElement self-verify failed; retrying with forceScreenshot" ,
3206+ ) ;
3207+ // The failed attempt's frames are untrusted BUT satisfy the
3208+ // completeness check — wipe them so the retry re-captures everything
3209+ // instead of silently keeping damaged files.
3210+ rmSync ( framesDir , { recursive : true , force : true } ) ;
3211+ mkdirSync ( framesDir , { recursive : true } ) ;
3212+ resetCaptureAttemptProgress ( job ) ;
3213+ dedupPerfs . length = 0 ;
3214+ cfg . useDrawElement = false ;
3215+ probeSession = null ;
3216+ capturePlan = replanAfterFailure ( capturePlan , { kind : "draw_element_verification" } ) ;
3217+ syncCapturePlan ( ) ;
3218+ updateCaptureObservability ( {
3219+ forceScreenshot : capturePlan . forceScreenshot ,
3220+ deSelfVerifyFallback,
3221+ deFallbackReason,
3222+ deFallbackFailedDb,
3223+ deFallbackFrameIndex,
3224+ deFallbackThresholdDb,
3225+ } ) ;
3226+ if ( capturePlan . kind !== "sdr_disk" ) {
3227+ throw new Error ( `Disk verify retry requires sdr_disk plan; got ${ capturePlan . kind } ` ) ;
3228+ }
3229+ captureRes = await invokeDiskCapture ( capturePlan ) ;
3230+ // The first attempt's error marked the phase failed; the retry
3231+ // recovered it — don't brand the render as failed in telemetry.
3232+ observability . clearFailure ( "capture_disk" ) ;
3233+ }
31753234 const captureFrameMs = Date . now ( ) - captureFrameStart ;
31763235 workerCount = captureRes . workerCount ;
31773236 updateCaptureObservability ( { workerCount } ) ;
0 commit comments