Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions packages/producer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ await assemble(

The three activity functions plus their result types are also re-exported from `@hyperframes/producer` so callers that pin the main package don't need a separate subpath import. Supported formats: `mp4` SDR, `mov` ProRes 4444, and `png-sequence`. webm and HDR mp4 trip a typed `FormatNotSupportedInDistributedError` — use the in-process renderer (`executeRenderJob`) for those.

See [`DISTRIBUTED-RENDERING-PLAN.md`](../../DISTRIBUTED-RENDERING-PLAN.md) for the full architecture.

## How it works

1. **Serve** — spins up a local file server for the HTML composition
Expand Down
6 changes: 3 additions & 3 deletions packages/producer/src/distributed.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* `@hyperframes/producer/distributed` — the distributed render primitives.
*
* See `DISTRIBUTED-RENDERING-PLAN.md` for the full architecture. The three
* activities (`plan` → `renderChunk` × N → `assemble`) are pure functions
* over local file paths; networking + orchestration live in adapters.
* The three activities (`plan` → `renderChunk` × N → `assemble`) are pure
* functions over local file paths; networking + orchestration live in
* adapters.
*
* Adopters (AWS Lambda, Cloud Run Jobs, Temporal, K8s Jobs, plain SSH):
*
Expand Down
15 changes: 8 additions & 7 deletions packages/producer/src/regression-harness-distributed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
* pass the same quality bar the in-process renderer passes against the
* same frozen baseline. A separate {@link DISTRIBUTED_SIMULATED_MIN_PSNR_DB}
* pathology floor catches the case where a fixture authored a permissive
* threshold and distributed regresses to fully-black output. The §5.1
* 50 dB target was written for per-render comparison (fresh in-process vs
* fresh distributed); against the frozen baseline file it's unreachable
* for either mode due to shared encoder/JPEG-capture jitter, so the
* harness can't use it as a per-test gate.
* threshold and distributed regresses to fully-black output. The 50 dB
* "distributed vs in-process" contract is a per-render comparison
* (fresh in-process vs fresh distributed); against the frozen baseline
* file it's unreachable for either mode due to shared encoder/JPEG-
* capture jitter, so the harness can't use it as a per-test gate.
*
* Not every fixture can run in distributed-simulated mode. Distributed mode
* refuses webm, HDR mp4, NTSC framerates, and non-{24,30,60} fps at plan
Expand All @@ -44,8 +44,9 @@ export type HarnessMode = "in-process" | "distributed-simulated";
* a chunk that renders fully-black against a fixture authored with a
* permissive `minPsnr`. Non-pathological drift is caught by the fixture's
* own threshold; both modes share the same encoder/JPEG-capture jitter
* floor against the frozen baseline file, so the §5.1 50 dB target is
* unreachable for either mode and isn't a useful per-test gate.
* floor against the frozen baseline file, so the 50 dB distributed-vs-
* in-process contract value is unreachable for either mode and isn't a
* useful per-test gate.
*/
export const DISTRIBUTED_SIMULATED_MIN_PSNR_DB = 10;

Expand Down
9 changes: 5 additions & 4 deletions packages/producer/src/services/render/stages/planHash.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* planHash — content-addressed hash for distributed render plans.
*
* See DISTRIBUTED-RENDERING-PLAN.md §4.2 for the contract:
* Hash contract:
*
* planHash = sha256(
* SCHEMA_PREFIX
Expand All @@ -14,9 +14,10 @@
* ⊕ fps ⊕ width ⊕ height ⊕ format
* )
*
* Two invocations with identical inputs MUST produce the same hash. Adapters
* use this to short-circuit `plan()` on workflow replay and to detect
* cross-version mismatches (§9.3 PLAN_HASH_MISMATCH).
* Two invocations with identical inputs MUST produce the same hash.
* Adapters use this to short-circuit `plan()` on workflow replay and to
* detect cross-version mismatches via a typed PLAN_HASH_MISMATCH error
* (defined in `errors.ts` and enumerated in `events.ts`).
*
* Pure utility; no caller exists yet — the distributed-render
* `services/distributed/plan.ts` will compose it.
Expand Down
8 changes: 5 additions & 3 deletions packages/producer/src/services/render/stages/probeStage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
* clean them up in its `finally` block.
*
* Hard constraints preserved verbatim from the in-process renderer:
* - `recompileWithResolutions` runs inside this stage because it depends
* on browser-resolved durations, even though §2.1 of the distributed
* plan lists recompile as a sibling phase.
* - `recompileWithResolutions` runs inside this stage because it
* depends on browser-resolved durations. (Distributed-pipeline
* callers can think of recompile as logically separate from probe,
* but the implementation co-locates them here because they share
* the browser session.)
* - `composition` (videos/audios/duration) is mutated in place — callers
* downstream see the reconciled view through the same object reference.
* - The stage computes the final composition `duration` and `totalFrames`
Expand Down
4 changes: 2 additions & 2 deletions packages/producer/src/services/renderOrchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1452,8 +1452,8 @@ export async function executeRenderJob(
// returned on `compileResult.forceScreenshot`. The sequencer stores it
// in a local `captureForceScreenshot` below; the BeginFrame calibration
// fallback updates the local — not `cfg` — and capture stages receive
// the value as an explicit parameter. See DISTRIBUTED-RENDERING-PLAN.md
// §4.3 (`LockedRenderConfig.forceScreenshot`).
// the value as an explicit parameter. This keeps `cfg` immutable for
// the rest of the pipeline.
const enableChunkedEncode = cfg.enableChunkedEncode;
const chunkedEncodeSize = cfg.chunkSizeFrames;
// Declared outside the try so `finally` can stop the interval, but
Expand Down
22 changes: 12 additions & 10 deletions packages/producer/tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,16 @@ passing in the summary):

Both modes use the fixture's authored `minPsnr` as the per-test
threshold — distributed must clear the same quality bar in-process
clears against the same frozen baseline. (`DISTRIBUTED-RENDERING-PLAN.md`
§5.1's 50 dB target is a per-render distributed-vs-in-process contract;
against the frozen baseline file, neither mode reaches it consistently
due to shared encoder/JPEG-capture jitter.) An absolute 10 dB pathology
floor catches fully-black-output regressions when a fixture authors a
permissive threshold. A distributed failure at the fixture's own
threshold means the distributed pipeline has drifted — file an issue
rather than relaxing the fixture.
clears against the same frozen baseline. (Internal contract: distributed
vs in-process renders of the same fixture should clear 50 dB PSNR
against each other within the same Docker image. Against the frozen
committed baseline, neither mode reaches that consistently due to
shared encoder/JPEG-capture jitter — that's why the fixture's authored
threshold gates here, not the 50 dB contract value.) An absolute 10 dB
pathology floor catches fully-black-output regressions when a fixture
authors a permissive threshold. A distributed failure at the fixture's
own threshold means the distributed pipeline has drifted — file an
issue rather than relaxing the fixture.

`--update` is incompatible with `--mode=distributed-simulated`: the
in-process renderer is the source of truth for baselines, and the
Expand Down Expand Up @@ -145,8 +147,8 @@ exercises one of:
- per-format chunk-boundary correctness (mp4 H.264, mp4 H.265, ProRes, png-sequence)
- per-adapter chunk-seam state preservation (GSAP, Anime.js, Three.js, Lottie, CSS, WAAPI)

See `DISTRIBUTED-RENDERING-PLAN.md` §10.2 for the equivalence axes each
distributed fixture covers.
Each distributed fixture covers one or more equivalence axes — see the
`meta.json` `description` field for what a given fixture is locking in.

### Fixture pattern (4.2 onward)

Expand Down
Loading