Skip to content

feat(effects): footage() reads the recording at its own resolution - #1396

Merged
EtienneLescot merged 1 commit into
mainfrom
feat/shader-fx-footage
Oct 26, 2026
Merged

EtienneLescot merged 1 commit into
mainfrom
feat/shader-fx-footage

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

Summary

Shader effects (Off-Menu) could only read frame(), the composed picture at the output's resolution. A recording shown smaller than its pixels has lost its detail there, so an effect that magnifies (a loupe, a lens, a close-up) only magnified blur.

This adds footage(p) to the effect contract: the recording itself at point p of its surface (the space of fx_to_footage), at the recording's own resolution, transparent outside 0..1. It holds the recording only: the cursor, webcam and annotations stay in frame().

  • Rendering. Each backend paints the content cut (content_cut) into an RGBA copy at the decoder texture's resolution, at most 4096 a side, with one mode-0 draw: the same YUV conversion as the drawn screen, on the model of the depth-of-field pyramid fill. Bound at t4 (D3D11), texture(1) (Metal), binding 4 (wgpu, an empty texture otherwise).
  • Cost. analyze() tells from naga's global usage whether fx_fs reads fx_footage; reads_footage() memoises it per source, and the copy is painted only on frames where such an effect is active. An effect that does not read it pays nothing. footage() reads count in the cost tier like frame() reads.
  • Contract text. The MCP resource, the addEffect description and docs/shader-effects.md describe it.

Related issue

None. Asked for by the maintainer to reproduce Screen Studio's Glass Loupe as an Off-Menu effect.

Type of change

  • Bug fix
  • Feature
  • Enhancement
  • Documentation
  • Refactor / maintenance
  • Performance
  • Security

Release impact

  • Patch
  • Minor
  • Major / breaking change
  • No release note needed

Desktop impact

  • Windows
  • macOS
  • Linux
  • Installer / packaging
  • Not platform-specific

Screenshots / video

A glass loupe written as an effect, exported at 1080p from a 3840×2160 take: read through frame() the magnified lines are thick and soft; read through footage() they are as thin and sharp as in the recording. Frames and the demo are in the maintainer's demo folder (not committed).

Testing

  • Windows (D3D11): cargo test -p openscreen-compositor --lib: 468 passed, 1 failed. The failure, pipeline_windows::tests::playback_off_the_end_of_the_programme_stays_on_the_last_clip, fails the same way with main's compositor files on this machine; it involves no shader effect.
  • New render tests (D3D11, Metal, wgpu): a 512 px source in one-pixel columns, shrunk under the padding: footage() reads each column white or black, nothing outside 0..1, while the composed picture no longer separates them; and an effect that does not read footage() never paints the copy.
  • Linux (wgpu, lavapipe under WSL): the footage tests pass (15/15).
  • macOS (Metal): compiled and run by the Rust test (macOS compositor) job; not run locally (no ffmpeg on the test Mac).
  • Contract: shader_fx unit tests (cost counting, copy size, FXC compile with t4, MSL texture(1)), npx vitest --run electron/mcp/effects-contract.test.ts electron/ai-edition/deep-agent (116 passed), both tsc configs.
  • Real app: the dev build drove a glass-loupe effect through the MCP (addEffect then exportVideo): it compiled at 26 to 30 reads (medium), and the 4K export of a 14 s project took 8.8 s against 9.2 s for the same loupe on frame().

Only the targeted test runs above, not the full npm run test: CI runs it.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Shader effects can now sample the original recording at its source resolution using footage(p), independently of the composed frame.
    • Sampling outside the recording area returns transparent pixels. The recording is copied only when an active effect uses this feature.
  • Documentation

    • Updated shader-effect guidance to explain how footage(p) differs from frame(uv) and how footage reads affect performance.

frame() holds the composed picture at the output's resolution, so an effect
that magnifies (a loupe, a lens) only magnified blur. footage(p) reads the
recording itself at point p of its surface, the space of fx_to_footage,
transparent outside 0..1.

Each backend paints the content cut into an RGBA copy at the decoder
texture's resolution (at most 4096 a side) with one mode-0 draw, the same
YUV conversion as the drawn screen, and binds it at t4 / texture(1) /
binding 4. analyze() tells from naga's global usage whether fx_fs reads
fx_footage; reads_footage() memoises it per source, so an effect that does
not read it pays nothing. footage() reads count in the cost tier like
frame() reads.
@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 63f1bf7d-89c1-4fd4-84c6-7c01d1c0f765
Commits

Reviewing files that changed from the base of the PR and between 2159f42 and 177f511.

Files selected for processing: 8
  • crates/compositor/src/compositor_linux.rs
  • crates/compositor/src/compositor_macos.rs
  • crates/compositor/src/compositor_windows.rs
  • crates/compositor/src/shader_fx.rs
  • docs/shader-effects.md
  • electron/ai-edition/deep-agent/service.ts
  • electron/mcp/effects-contract.test.ts
  • electron/mcp/effects-contract.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Walkthrough

Shader effects can use footage(p) to sample the source recording at its own resolution, with transparent results outside its bounds. Linux, macOS, and Windows compositors create and cache a footage copy only when an active effect reads it. Effect analysis, bindings, cost guidance, and tests now include footage reads.

Changes: Footage sampling

Layer / File(s) Summary
Shader contract and usage analysis
crates/compositor/src/shader_fx.rs, docs/shader-effects.md, electron/mcp/effects-contract.ts, electron/mcp/effects-contract.test.ts, electron/ai-edition/deep-agent/service.ts
The shader contract adds footage(p), and analysis detects whether an effect reads it. Platform bindings, cost guidance, and effect descriptions include footage reads.
Backend footage-copy targets
crates/compositor/src/compositor_linux.rs, crates/compositor/src/compositor_macos.rs, crates/compositor/src/compositor_windows.rs
Each compositor caches an RGBA target for the cropped source recording and recreates it when its dimensions change.
Effect binding and composition
crates/compositor/src/compositor_linux.rs, crates/compositor/src/compositor_macos.rs, crates/compositor/src/compositor_windows.rs
Compositors render a footage copy only when an active effect reads it. They pass the optional texture to image, text, and both-target effects. Tests cover source-resolution sampling, transparent out-of-bounds samples, and skipped copies.

Priority: Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Compose as compose_frame
  participant Analysis as reads_footage
  participant Copy as encode_footage_copy or fill_footage_copy
  participant Effect as encode_shader_effect or apply_shader_effect
  Compose->>Analysis: Check active effects for footage reads
  alt An active effect reads footage
    Compose->>Copy: Render or reuse the source-resolution copy
    Copy-->>Compose: Return the footage texture
  else No active effect reads footage
    Compose->>Compose: Keep footage texture absent
  end
  Compose->>Effect: Pass the optional footage texture
Loading

Possibly related issues

  • getopenscreen/openscreen issue 1178 requests precise cropping and mentions magnification as a possible solution. This change enables source-resolution sampling for magnification effects but does not add crop controls or a cropping UI.

Possibly related PRs

  • getopenscreen/openscreen#1364: Adds an MCP effects-contract recipe that maps UI block coordinates through footage coordinates; this change defines the footage(p) sampling space used by that recipe.
Merge Risk: Minimal · up to 177f5

The change is mergeable after normal checks; no concrete issue requiring a pre-merge fix was established.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 74.36% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 39 functions across 7 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: adding footage() support for reading the recording at its own resolution.
Description check Passed The description includes the required summary, issue context, change type, release impact, platform impact, visual-change note, and detailed testing results. It is complete enough for review, although…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description includes the required summary, issue context, change type, release impact, platform impact, visual-change note, and detailed testing results. It is complete enough for review, although the referenced screenshots and demo are not included in the pull request.

Full details: Docstring Coverage

Explanation

Docstring coverage is 74.36% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 39 functions across 7 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

🔧 Clippy (1.99.0)

Clippy execution failed

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@EtienneLescot
EtienneLescot merged commit c85e3c7 into main Oct 26, 2026
21 checks passed
@EtienneLescot
EtienneLescot deleted the feat/shader-fx-footage branch October 26, 2026 10:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant