RenderLifecycleMixin
Auto-generated @jbrowse/mobx-state-tree API for the current JBrowse release — see pluggable elements for concepts. Built into JBrowse core. View source.
Owns the GPU draw lifecycle for any display that paints to a canvas.
Plugins compose this mixin (directly or via MultiRegionDisplayMixin /
GlobalFetchMixin) and call
self.attachRenderingBackend(backend, () => ({ upload, render })) from their
own startRenderingBackend(backend) action. The second argument is a thunk
because it runs exactly once, on the first attach: startRenderingBackend
fires again on every context-loss recovery, and the autoruns keep the callbacks
they were given first. Anything the callbacks close over — an upload sync's memo
of what it last sent — is built inside it, so it lives as long as the callbacks
that read it rather than being allocated per recovery and discarded. The mixin
owns:
canvasDrawn— observable flag read by test-selectordata-testidattributes to detect first paint.currentRenderingBackend— the backend reference, updated on context-loss recovery. Autoruns read it each tick so they re-fire against the new one without being reinstalled.renderTick— counter the render autorun observes; bumped byrenderNow()(tab-visibility restore) and after every upload (ensures render re-fires when an upload happens but renderState identity stays stable).autorunsInstalled— guardsattachRenderingBackendso the autorun pair is spawned once per model instance, not once per backend assignment.
The upload callback runs in one autorun, render in another. Inside each,
every observable read is auto-tracked by MobX — no getter-layer indirection, no
multi-entry config. render returns true when the backend actually painted
content (flips canvasDrawn), false to skip this tick (e.g. renderState not
yet computed or no regions loaded).
Volatiles
| Member | Description |
|---|---|
canvasDrawncanvasDrawn: false | flips true on first paint; read by test selectors to detect render |
currentRenderingBackendcurrentRenderingBackend: undefined | current backend reference, updated on context-loss recovery. Typed unknown (not generic B) on purpose: this mixin is composed by every display via a non-generic factory, so the per-display backend type B isn't known here — it's supplied at attachRenderingBackend<B> and narrowed with as B inside the autoruns. Don't "fix" the cast. |
renderTickrenderTick: 0 | counter the render autorun observes; bumped to force a re-render |
autorunsInstalledautorunsInstalled: false | guards attachRenderingBackend so the autorun pair spawns once per instance |
renderErrorrenderError: undefined | the render-backend (GPU/Canvas2D init or context-loss) error, or undefined. Single source of truth for the render-error terminal state: useRenderingBackend writes it from the canvas-init mechanism so the model — not React-local hook state — owns every terminal state. Read by displayPhase (whose renderError term outranks loading, suppressing the scrim) and by DisplayChrome (shows the retry overlay). |
Getters
| Member | Description |
|---|---|
canRenderboolean | Overridable precondition (default true): both lifecycle autoruns skip their callback entirely while this is false, so a display never has to open its own upload/render with a readiness check.The LGV mixins override it with view.initialized, because before the view is measured its geometry throws by design (view.width, and so visibleRegions / trackWidthPx with it) and a throw in either callback is routed to renderError — surfacing "not measured yet" as the GPU render-error banner. Because it's an observable read inside the autoruns, the pair re-fires the moment it flips. This is the precondition axis only: whether any data has arrived stays the render callback's own gate (return false to skip a tick), which is why renderState getters can be plain resolved values. |
rendersCanvasboolean | Overridable hook (default true): whether this display paints a canvas in its current configuration, as opposed to a deliberate static placeholder (LD with the triangle off, sequence past base resolution — both render a message where the <canvas> would go, so canvasRef is never called and canvasDrawn can never flip).Lives here, beside canvasDrawn, because every consumer of "has this display painted" needs the pair — and until 2026-08 each family declared its own copy (per-region hard-coded true, global carried the hook for LD), so a display could express the state only to whichever family it happened to compose. See painted below for the reader that was missed. |
paintInertboolean | Overridable hook (default false): the display has reached a state it will not paint its way out of, so painted below should answer finished rather than pending. Both LGV fetch families fill it with !!error — a fetch that failed before first paint keeps its canvas mounted, since the error bar is an overlay rather than a subtree replacement, so nothing ever draws into it. Named for fetchInert on the comparative side.A hook rather than a read of error here, for two reasons that both bite: this package is a leaf and error belongs to the fetch mixins, and declaring that name here would collide with FetchMixin's volatile — types.compose gives the collision to its later argument, and the two families compose the two mixins in opposite orders. |
paintedboolean | The first-paint answer every consumer outside the display should read, canvasDrawn being only the raw flag: a display that is deliberately not painting a canvas has finished, and saying otherwise is a lie that never resolves.The two rendersCanvas: false states each had three of their four consumers wired by hand — the loading scrim (rendersCanvas / fetchInert) and the SVG export (fetchInert) — while the fourth, data-display-drawn, went on publishing "false" forever off the raw flag. That attribute is what PENDING_DISPLAYS (@jbrowse/browser-test-utils) selects on, so a zoomed-out reference sequence track made every waitForDisplaysDone on the page burn its full timeout — silently, since that wait swallows its own. Same shape as fetchInert on the comparative side: the reader you forget is the one outside the display, so the display has to publish one name for it.paintInert is the third term and the same argument once more, for the state where a display would paint a canvas and never gets to — a fetch that failed before first paint. See that hook. |
Actions
| Member | Description |
|---|---|
markCanvasDrawn() => void | |
resetCanvasDrawn() => void | |
stopRenderingBackend() => void | |
renderNow() => void | |
setRenderError(error: unknown) => void | set/clear the render-backend error. Called by useRenderingBackend: with the error when the canvas factory rejects (or context-loss re-init fails), and with undefined on successful (re)init and on retry. |
| attachRenderingBackend | attach a GPU/Canvas2D backend and install the upload + render autorun pair. Idempotent: re-calling swaps the backend and does not run setup again, so the callbacks and everything they close over are the first call's. |