Expand description
GPU renderer: the effect chain as OpenGL shaders, using eframe’s existing glow context.
Why: the CPU compositor is fine for cuts but blur/VHS/motion-blur at 1080p+ are not. Every round-3 effect is a fragment shader here; the CPU path (engine/effects.rs) keeps the older, cheap effects so the app still runs (degraded) when no GL context exists.
Threading: GL belongs to the thread that owns the context — the UI thread. So:
- preview →
render_to_textureduringApp::update, painted straight into the preview pane (no CPU readback at all). - export → the export thread posts
RenderRequests; the UI thread serves them withrender_frame(readback viaglReadPixels) so preview and export run the SAME shaders. - headless (
--selftest, no GL) → callers fall back toengine::compose::Compositor.
Pipeline per frame: decode (CPU, player threads) → upload layer textures (upload) → for each visual
clip: build its chain (node graph when present, else the linear effect stack) → ping-pong FBOs →
transform+blend into the canvas FBO → adjustment layers re-process the canvas → subtitles/markers.
All textures are RGBA8 with straight alpha, matching media::Frame.
What the caller owes us (LayerSet): one decoded frame per visual clip that is on screen at t,
keyed by clip id — including both clips of a transition (they are rendered virtually extended past
their own bounds), the rendered bitmap of a Text/Shape clip, the rendered canvas of a nested
Sequence clip, and, under LayerSet::SUBTITLES, the subtitle bitmap. Anything missing is skipped.
Footage is placed from its asset’s native size, so any decode size works; text/shape/subtitle
bitmaps are placed 1:1 and must be rasterised for render_size(w, h, quality), not for w×h.
Textures returned by render_to_texture / apply_effect / mask_texture / eval_graph stay valid
until the next render_to_texture / render_frame call, which recycles them.
Structs§
- Extra 🔒
- Per-clip render tweaks used by transitions (mirrors
compose::Extra). - GpuRenderer
- The renderer. One per app; lives on the UI thread.
- Layer
Set - Decoded layers for one timeline instant, produced by the player/export thread on the CPU and handed to the renderer: (clip id, decoded frame). Motion-blur effects also get the neighbouring frames.
- Layer
Tex 🔒 - A layer texture and the revision of the frame currently in it.
- Pool 🔒
- Size-keyed target pool. GL objects are created by the renderer when
takefinds nothing reusable and deleted here when the free list outgrows its budget. Ping-pong falls out of it:compositetakes a second canvas-sized target, draws into it and returns the old canvas here, so the two keep swapping. - Prog 🔒
- Programs
- Compiled shader programs, cached by effect kind (and by source hash for
EffectKind::Shader). - Target
- One reusable off-screen target (texture + FBO).
Enums§
- MaskTex 🔒
- Either a mask we rendered (and must recycle) or one the caller owns.
Constants§
- COPY_
BODY 🔒 vec4 effect(..)bodies that are not per-effect: a pass-through and the two-input matte.- EFFECT_
BASE 🔒 - K_
COMPOSITE 🔒 - K_COPY 🔒
- K_MASK 🔒
- K_MATTE 🔒
- MATTE_
BODY 🔒 - MAX_
MASK_ POINTS MASK’sm_pointsarray size.- MOTION_
NEXT 🔒 - MOTION_
PREV 🔒 - Texture-cache keys for the neighbouring frames of a motion-blurred clip. Clip ids are small
counters (
Project::new_id), so the top bits are free to tag a variant of the same clip. - PARAM_
NAMES 🔒 - One name per parameter any effect can have (
Curveshas 12, everything else 8 or fewer). - UNIFORMS 🔒
- Every uniform any of our programs can declare; looked up once per link.
- USER_
BIT 🔒 - U_B 🔒
- U_DST 🔒
- U_MASK 🔒
- U_NEXT 🔒
- U_PREV 🔒
- U_TEX 🔒
- Texture units.
Functions§
- blend_
index - The GLSL
int modefor a blend mode: its index inBlendMode::ALL, which is the orderengine::blend::blend_channelmatches. - build 🔒 ⚠
- column_
major 🔒 - Row-major 3×3 -> the column-major order
glUniformMatrix3fvexpects. - compile 🔒 ⚠
- driven
- An effect node’s parameter ports: a value node wired to port
i + 1computes parameterifor this frame, which is the graph’s only way to drive a knob. Untouched effects are not cloned. - hash_
str 🔒 - inverse_
placement - Canvas pixels -> layer uv for a placement (what
COMPOSITE’su_invwants). - invert3
- Inverse of a 3×3 (adjugate / det); None when singular.
- param_
values 🔒 - An effect’s parameters at time
t, inEffectKind::params()order. - placement_
quad - The placed layer’s four corners (TL, TR, BR, BL) in canvas pixels, projected through the
yaw/pitch tilt with focal length
fand depth offsetz0.Nonewhen a corner is behind the camera. Mirrorscompose::draw_layer_perspective(which uses f = canvas width, z0 = 0). - render_
size - The canvas size
render_to_textureactually renders at for a given preview quality. Callers must rasterise text/shape/subtitle layers at this size (they are placed 1:1, not fitted). - scaler_
index - square_
to_ quad - Homography mapping the unit square (0,0)(1,0)(1,1)(0,1) onto
q(Heckbert). ponytail: copied fromcompose::square_to_quad(private there) so the GPU geometry cannot drift from the CPU one — make that pairpub(crate)and this goes away. - tex_
params 🔒 ⚠ - user_
key 🔒 - Cache key of a user shader body — shared by
effect_program,check_shaderandshader_errorso the editor reads the very program the renderer would build.