Skip to content

fix(compositor): rendre le repli CPU Linux forçable, testé et garanti - #223

Merged
EtienneLescot merged 2 commits into
release/v1.8.0from
fix/linux-cpu-backend-forceable
Aug 1, 2026
Merged

fix(compositor): rendre le repli CPU Linux forçable, testé et garanti#223
EtienneLescot merged 2 commits into
release/v1.8.0from
fix/linux-cpu-backend-forceable

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Le repli logiciel existait déjà sur Linux — mais par accident. create_backend ignorait son paramètre, et wgpu rendait lavapipe de lui-même quand c'était le seul ICD. Ça marche, et c'est exactement le problème : rien ne pouvait l'exercer, rien ne le signalait, rien ne le garantissait.

Trois conséquences, toutes fermées ici.

1. Le chemin CPU n'était pas testable

create_backend(_backend) ignorait son argument : impossible de demander lavapipe sur une machine qui a un GPU. Le seul levier restant était VK_DRIVER_FILES, qui agit sur le processus entier — sous Electron il prive aussi Chromium de son GPU, qui rastérise alors toute son UI sur CPU et sature la machine. Vérifié à mes dépens pendant cette PR : le test est inexploitable et emporte les autres applications Electron du poste.

  • Backend::Cpu passe désormais par force_fallback_adapter, Backend::Hardware rejette explicitement un adaptateur logiciel.
  • OPENSCREEN_COMPOSITOR_BACKEND=hardware|cpu force le choix sans toucher au loader Vulkan — seul notre compositeur bascule. Même motif que OPENSCREEN_EXPORT_ENCODER.
  • Effet de bord utile : create devient réellement matériel strict. Un golden mesuré sur llvmpipe passait jusqu'ici pour une mesure GPU.

2. Rien ne le signalait

Windows loggue son repli ; Linux ne loggait rien, et diagnose() n'était que format!("{err:#}"). Un hôte tombé sur lavapipe rendait à quelques fps sans qu'aucun log ne permette de l'établir à distance.

  • L'adaptateur retenu est journalisé, comme côté Windows.
  • diagnose() sépare « aucun ICD Vulkan installé » du reste et nomme le paquet à installer. C'est la seule panne de cette famille que l'utilisateur peut réparer lui-même, et elle s'affichait en « Aperçu indisponible sur cette machine », sans piste.
  • classify s'appuie sur DeviceType::Cpu — ce que l'ICD déclare — plutôt que sur une sous-chaîne du nom, qui reste en filet.

3. Rien ne le garantissait

electron-builder.json5 ne déclarait aucune dépendance. Sans mesa-vulkan-drivers, aucun adaptateur : probe() répond "none", que le TS traite comme « pas d'addon » et qui n'affiche donc aucune notice.

  • .debmesa-vulkan-drivers, pacmanvulkan-swrast. depends remplace la liste par défaut d'electron-builder au lieu de s'y ajouter, donc les valeurs par défaut sont reprises verbatim, la nôtre en dernier.
  • L'AppImage n'a pas de mécanisme de dépendances et reste exposée : c'est pour elle que diagnose() nomme le paquet.

Le job CI qui manquait

Le Rust Linux n'était compilé nulle part : ci.yml couvrait macOS (test) et Windows (check), et les 2154 lignes du moteur wgpu ne passaient que par le poste des contributeurs. Le nouveau job installe lavapipe, donc il exerce pour de vrai le backend CPU sur un runner sans GPU — OPENSCREEN_REQUIRE_CPU_BACKEND=1 le fait échouer plutôt que sauter s'il ne l'obtient pas.

Il a immédiatement trouvé deux choses, sans lesquelles il ne peut pas exister :

  • export_timing.rs et output_geometry_golden.rs ne compilaient pas sous Linux — ils appellent probe_frame_count / readback_resized, qui n'existent que côté Windows et macOS. Les fichiers de tests/ étant compilés quelle que soit la plateforme, le crate entier était incompilable en --tests sur Linux, en silence. Gardés en cfg(not(target_os = "linux")) et non cfg(windows), pour ne pas les retirer du job macOS qui les compile aujourd'hui.
  • L'export GIF construisait son device avec Gpu::create (matériel strict) alors que son propre commentaire dit suivre l'export MP4, qui prend create_auto. Un hôte sans GPU exportait donc un MP4 mais pas un GIF — sur le seul chemin où le backend CPU existe précisément pour que l'export aboutisse. Bug pré-existant, y compris sur Windows ; le corriger était aussi nécessaire pour que create devienne strict sans casser les machines lavapipe qui fonctionnent aujourd'hui.

Ce que cette PR ne fait pas

Elle ne construit pas un backend CPU : elle rend vérifiable et garanti celui qui existait. Elle ne touche ni au décodage ni à l'encodage Linux, tous deux logiciels par construction (pas de VAAPI) — c'est un chantier distinct.

Related issue

n/a — issu de l'audit de l'état de #162, dont les 4 commits sont sur main depuis le port multi-plateforme.

Type of change

  • Bug fix

Release impact

  • Patch

Desktop impact

  • Linux
  • Installer / packaging

Testing

Sur Ubuntu 24.04, AMD Radeon 610M (RADV RAPHAEL_MENDOCINO) + lavapipe installé :

cargo test -p openscreen-compositor --lib --tests
test result: ok. 118 passed  (lib)
test result: ok. 17 passed   (remux_seek_index)
test result: ok. 3 passed    (compose_linux, skip opt-in)
test result: ok. 3 passed    (cpu_backend_linux)

Le forçage vérifié sur une machine qui a un GPU, sans toucher au Vulkan de la session :

[d3d] adaptateur Vulkan : llvmpipe (LLVM 20.1.2, 256 bits) (Cpu, Vulkan) -> backend Cpu
[d3d] adaptateur Vulkan : AMD Radeon 610M (RADV RAPHAEL_MENDOCINO) (IntegratedGpu, Vulkan) -> backend Hardware

cargo build -p compositor-view-napi --release passe également.

Non vérifié localement : le packaging. Les listes depends sont reprises de app-builder-lib (FpmTarget.getDefaultDepends, electron-builder 26.8.1) mais aucun .deb ni .pacman n'a été construit ici — à confirmer sur le premier build de release.

Summary by CodeRabbit

  • New Features

    • Added Linux compositor support with automatic hardware or CPU rendering fallback.
    • Added Vulkan runtime requirements to Linux packages for improved compatibility.
    • GIF exports can now complete when no hardware GPU is available.
  • Bug Fixes

    • Improved Linux graphics backend selection, diagnostics, and fallback behavior.
  • Tests

    • Added Linux coverage for CPU and hardware rendering paths.
    • Updated platform-specific tests to run only where supported.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fc8d4df1-a723-4c98-a5d8-147ab4a6e695

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Linux compositor support now includes explicit hardware and CPU backend selection, automatic CPU fallback, adapter diagnostics, Linux validation, CI coverage, and Vulkan runtime packaging. GIF export uses automatic GPU creation.

Changes

Linux compositor support

Layer / File(s) Summary
Backend selection and diagnostics
crates/compositor/src/d3d_linux.rs
Linux GPU creation supports hardware and CPU overrides, adapter classification, fallback selection, diagnostics, and unit tests.
Export integration and Linux validation
crates/compositor-view-napi/src/lib.rs, crates/compositor/tests/cpu_backend_linux.rs, crates/compositor/tests/export_timing.rs, crates/compositor/tests/output_geometry_golden.rs
GIF export uses automatic GPU selection. Linux tests validate backend behavior, while unsupported platform-specific tests are excluded.
Linux CI and runtime packaging
.github/workflows/ci.yml, electron-builder.json5
Linux CI installs Vulkan and FFmpeg requirements, runs compositor checks and the N-API release build, and adds Vulkan runtime packages for Debian and Pacman targets.

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

Sequence Diagram(s)

sequenceDiagram
  participant create_auto
  participant create_backend
  participant VulkanAdapter
  create_auto->>create_backend: request hardware or CPU backend
  create_backend->>VulkanAdapter: create adapter with fallback settings
  VulkanAdapter-->>create_backend: adapter details and device type
  create_backend-->>create_auto: GPU or diagnostic error
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Title check ✅ Passed Le titre décrit clairement le changement principal : rendre le repli CPU Linux forçable, testé et garanti.
Description check ✅ Passed La description couvre les sections requises, les impacts, les tests effectués et la limite connue du packaging non vérifié.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/linux-cpu-backend-forceable

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 235-242: Update the “Resolve the toolchain paths” step to derive
libclang from the installed libclang-dev package, using dpkg -L (or an
equivalent deterministic highest-version selection) instead of find with head
-1. Preserve the existing empty-path validation and LIBCLANG_PATH export, while
ensuring the selected library matches the freshly installed package.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a1a40529-548d-4a57-a59b-0476492201e9

📥 Commits

Reviewing files that changed from the base of the PR and between 3f0ec8a and 2200565.

📒 Files selected for processing (7)
  • .github/workflows/ci.yml
  • crates/compositor-view-napi/src/lib.rs
  • crates/compositor/src/d3d_linux.rs
  • crates/compositor/tests/cpu_backend_linux.rs
  • crates/compositor/tests/export_timing.rs
  • crates/compositor/tests/output_geometry_golden.rs
  • electron-builder.json5

Comment thread .github/workflows/ci.yml
EtienneLescot added a commit that referenced this pull request Aug 1, 2026
…Linux

`find | head -1` prenait le premier résultat dans l'ordre de parcours du
système de fichiers, qui n'est pas trié. L'image ubuntu-latest embarque
plusieurs LLVM : si l'un d'eux a un paquet -dev préinstallé, on pouvait
sélectionner une version autre que celle qu'apt vient d'installer. Les headers
built-in de clang étant liés à la version de libclang, le symptôme aurait été
`stddef.h file not found` — une erreur qui ne désigne pas sa cause.

`sort -V | tail -1` prend la plus récente, de façon déterministe.

Remonté par CodeRabbit sur #223.
@EtienneLescot
EtienneLescot changed the base branch from main to release/v1.8.0 August 1, 2026 12:55
Le repli logiciel existait déjà sur Linux, mais par accident : `create_backend`
ignorait son paramètre et wgpu rendait lavapipe de lui-même quand c'était le seul
ICD. Ça marche — et c'est précisément le problème, parce que rien ne pouvait
l'exercer, rien ne le signalait et rien ne le garantissait.

- `create_backend` honore enfin son paramètre. `Backend::Cpu` passe par
  `force_fallback_adapter` ; `Backend::Hardware` rejette explicitement un
  adaptateur logiciel, ce qui rend `create` réellement matériel strict (un golden
  mesuré sur llvmpipe passait jusqu'ici pour une mesure GPU). `create_auto` gagne
  le repli explicite Hardware -> Cpu, comme côté Windows.

- `OPENSCREEN_COMPOSITOR_BACKEND=hardware|cpu` force le choix. `VK_DRIVER_FILES`
  ferait la même chose au niveau du loader Vulkan, mais s'applique au processus
  entier : sous Electron il prive aussi Chromium de son GPU, qui rastérise alors
  toute son UI sur CPU et sature la machine. Le chemin CPU n'était donc pas
  testable sans casser la session. Même motif que `OPENSCREEN_EXPORT_ENCODER`.

- L'adaptateur retenu est journalisé. Windows loggue son repli, Linux ne loggait
  rien : un hôte tombé sur lavapipe rendait à quelques fps sans que rien ne
  permette de l'établir à distance.

- `diagnose()` sépare « aucun ICD Vulkan installé » du reste et nomme le paquet à
  installer. C'est la seule panne de cette famille que l'utilisateur peut réparer
  lui-même, et elle s'affichait en « Aperçu indisponible sur cette machine ».

- `classify` s'appuie sur `DeviceType::Cpu` — ce que l'ICD déclare — plutôt que
  sur une sous-chaîne du nom ; le nom reste en filet.

- Le .deb et le pacman déclarent Mesa (`mesa-vulkan-drivers` / `vulkan-swrast`).
  Aucune dépendance n'était déclarée, donc rien ne garantissait qu'un ICD existe.

- Nouveau job CI `Rust test (Linux compositor)`. Le Rust Linux n'était compilé
  nulle part : la CI couvrait macOS et Windows, et les 2154 lignes du moteur wgpu
  ne passaient que par le poste des contributeurs. Le job installe lavapipe, donc
  il exerce pour de vrai le backend CPU sur un runner sans GPU.

Deux corrections que ce job a révélées, et sans lesquelles il ne peut pas exister :

- `export_timing.rs` et `output_geometry_golden.rs` ne compilaient pas sous Linux
  (`probe_frame_count` / `readback_resized` n'existent que côté Windows et macOS).
  Les fichiers de `tests/` étant compilés quelle que soit la plateforme, le crate
  entier était incompilable en `--tests` sur Linux, en silence. Gardés en
  `cfg(not(target_os = "linux"))` plutôt qu'en `cfg(windows)`, pour ne pas les
  retirer du job macOS qui les compile aujourd'hui.

- L'export GIF construisait son device avec `Gpu::create` (matériel strict) alors
  que son propre commentaire dit suivre l'export MP4, qui prend `create_auto`. Un
  hôte sans GPU exportait donc un MP4 mais pas un GIF — sur le seul chemin où le
  backend CPU existe précisément pour que l'export aboutisse. Le rendre strict
  sur Linux sans ce correctif aurait cassé le GIF sur les machines lavapipe qui
  fonctionnent aujourd'hui.
…Linux

`find | head -1` prenait le premier résultat dans l'ordre de parcours du
système de fichiers, qui n'est pas trié. L'image ubuntu-latest embarque
plusieurs LLVM : si l'un d'eux a un paquet -dev préinstallé, on pouvait
sélectionner une version autre que celle qu'apt vient d'installer. Les headers
built-in de clang étant liés à la version de libclang, le symptôme aurait été
`stddef.h file not found` — une erreur qui ne désigne pas sa cause.

`sort -V | tail -1` prend la plus récente, de façon déterministe.

Remonté par CodeRabbit sur #223.
@EtienneLescot
EtienneLescot force-pushed the fix/linux-cpu-backend-forceable branch from aeaa754 to 7ceb892 Compare August 1, 2026 13:01
@EtienneLescot
EtienneLescot merged commit 3993e45 into release/v1.8.0 Aug 1, 2026
16 checks passed
@EtienneLescot
EtienneLescot deleted the fix/linux-cpu-backend-forceable branch August 1, 2026 13:06
EtienneLescot added a commit that referenced this pull request Aug 1, 2026
…Linux

`find | head -1` prenait le premier résultat dans l'ordre de parcours du
système de fichiers, qui n'est pas trié. L'image ubuntu-latest embarque
plusieurs LLVM : si l'un d'eux a un paquet -dev préinstallé, on pouvait
sélectionner une version autre que celle qu'apt vient d'installer. Les headers
built-in de clang étant liés à la version de libclang, le symptôme aurait été
`stddef.h file not found` — une erreur qui ne désigne pas sa cause.

`sort -V | tail -1` prend la plus récente, de façon déterministe.

Remonté par CodeRabbit sur #223.
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