Skip to content

refactor(cardwired): rework GPU enumeration, add discrete GPU detection and fix switcheroo - #144

Merged
luytan merged 28 commits into
mainfrom
rework-gpu-detection
Aug 5, 2026
Merged

refactor(cardwired): rework GPU enumeration, add discrete GPU detection and fix switcheroo#144
luytan merged 28 commits into
mainfrom
rework-gpu-detection

Conversation

@luytan

@luytan luytan commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Description

Rework cardwired's GPU enumeration to be more consistent, more verbose, detect Discrete GPU and fix the switcheroo shim

Changed

  • GPU Enumeration: replaced sys-fs only discovery with a new struct named GpuEnumerator. It uses udev for the GPU node IDs (renderD/card), use Vulkan + EGL as fallback for discrete/virtual detection.
  • GpuDevice Model: added discrete, vfio, virtual_gpu and available flags. Unaivalable GPUs are excluded from cardwire logic but kept for listing
  • Modes: Integrated/Smart switch is now refused unless there's a real offload dGPU (discrete + non-default), these modes were only made for laptops. Also mode switching can get refused if a monitor is connected to the offload dGPU
  • Switcheroo shim: env vars computed per-GPU (default/offload/discrete/multi-gpus), added old switcheroo envs (DRI_PRIME, __NVIDIA)
  • Per-GPU-Block: can get refused if a display is connedted
  • block_nvidia_inodes(): function get skipped if the Nvidia GPU is the default display
  • Hybrid mode: unblock ALL GPUs, works on all systems

CARDWIRE_FORCE_GPU= is only implemented in cardwire-ebpf and switcheroo shim but is only honored in smart mode, manual mode per-gpu forcing will come in a next PR

Tests

Laptop

Hybrid(iGPU + dGPU) without asus-mux:
image

Integrated using asus dgpu_disable (also showcasing auto gpu_refresh working):
image

Ultimate (dGPU as default, iGPU as second) with asus-mux
image

Desktop

Hybrid, dGPU as default and iGPU as secondary

image

Only dGPU (iGPU disabled in uefi)
image

Proxmox VM with VFIO

image

TODO

  • Fix Switcheroo D-Bus environment variables for multi-GPU desktops (where dGPU is default)
  • Update mode.rs to target offload dGPUs explicitly and guard against blocking primary dGPUs on desktops
  • Update monitor_display.rs to target secondary discrete GPUs rather than !is_default
  • Add unit tests for desktop (dGPU default) and laptop (iGPU default) scenarios
  • Update Cardwire's DBUS to expose the new attributes
  • Update the CLI and the GUI to use these new attributes
  • Update mdBook documentation to reflect multi-GPU desktop behavior

Checklist:

  • My code follows the style guidelines of this project (cargo fmt)
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the mdBook documentation
  • My changes generate no new warnings (clippy/clang)
  • New and existing unit tests pass locally with my changes (either use nix flake check or wait for the ci)

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Enhanced GPU enumeration and status reporting (discrete, virtual GPU, VFIO, availability), including vendor and driver details in the CLI and GUI.
    • Improved Switcheroo environment generation for hybrid/offload setups.
  • Bug Fixes
    • GPU blocking/unblocking and mode selection now ignore unavailable devices and reject unsafe display-connected scenarios.
    • Hotplug reconciliation is improved, with targeted reapplication and safer fallbacks.
    • External-display probing now requires a valid dual-available-GPU topology.
  • Chores
    • Expanded CI Rust build/test dependencies for graphics and display libraries.

Walkthrough

The PR replaces legacy GPU discovery with Vulkan- and EGL-assisted enumeration. It adds GPU availability and capability state, updates display-mode and watcher handling, filters unavailable devices, and generates vendor-specific Switcheroo environment variables. Build environments now provide the required graphics libraries.

Changes

GPU enumeration and routing

Layer / File(s) Summary
Graphics dependencies and build environment
.github/workflows/cicd.yml, Cargo.toml, crates/cardwire-daemon/Cargo.toml, flake.nix, nix/default.nix
Build and runtime configuration adds Vulkan, EGL, GLVND, X11, Wayland, and XCB dependencies.
Vulkan, EGL, and GPU discovery
crates/cardwire-daemon/src/core/gpu/*
GpuEnumerator replaces the legacy discovery path. Vulkan and EGL resolve physical devices and discrete status. DRM, vendor, display, default-GPU, VFIO, availability, and virtual-GPU data are recorded in GpuDevice.
Daemon and display-mode integration
crates/cardwire-daemon/src/daemon.rs, crates/cardwire-daemon/src/interface/{debug,gpu,mode}.rs, crates/cardwire-daemon/src/models.rs, crates/cardwire-daemon/src/tasks/monitor_display.rs
Discovery, power watchers, GPU blocking, mode application, and display monitoring now operate on available GPUs and validate discrete-GPU and connected-display state.
Switcheroo routing and client metadata
crates/cardwire-daemon/src/interface/switcheroo.rs, crates/cardwire-cli/src/*, crates/cardwire-gui/src/*
Unavailable GPUs are excluded from routing and controls. D-Bus, CLI, and GUI models expose discrete, virtual, availability, vendor, and driver fields.

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

Sequence Diagram(s)

sequenceDiagram
  participant DaemonManager
  participant GpuEnumerator
  participant DisplayMode as display mode
  participant Switcheroo
  participant Clients as CLI and GUI
  DaemonManager->>GpuEnumerator: enumerate PCI graphics devices
  GpuEnumerator-->>DaemonManager: return GPU records with availability and capability flags
  DaemonManager->>DisplayMode: apply mode using available GPUs
  DisplayMode->>DisplayMode: validate discrete GPU and connected-display state
  DaemonManager->>Switcheroo: publish available GPU properties
  Switcheroo-->>Clients: provide routing and GPU metadata
Loading

Possibly related PRs

Suggested reviewers: juandelpueblo

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the GPU enumeration refactor, discrete GPU detection, and Switcheroo changes.
Description check ✅ Passed The description explains the changes, motivations, testing scenarios, TODOs, and checklist, but several checklist items remain unchecked.
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.

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

@luytan luytan linked an issue Aug 4, 2026 that may be closed by this pull request
@luytan
luytan marked this pull request as ready for review August 4, 2026 19:30

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
crates/cardwire-daemon/src/core/gpu/models.rs (1)

139-166: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Bind these four booleans to names before they hex a call site.

GpuDevice::new now accepts four adjacent unnamed bool values. Any transposition of discrete, vfio, available, or virtual_gpu at a call site still compiles, and the result changes real routing: is_available gates Switcheroo exposure, mode gating in crates/cardwire-daemon/src/interface/mode.rs, and the block guard in crates/cardwire-daemon/src/interface/gpu.rs. The #[allow(clippy::too_many_arguments)] marker is the lint telling you the same thing.

Pass a named flags struct instead. The allow attribute then disappears, and each call site states its intent.

♻️ Proposed shape
+#[derive(Debug, Clone, Copy, Default)]
+pub struct GpuFlags {
+    pub discrete: bool,
+    pub vfio: bool,
+    pub available: bool,
+    pub virtual_gpu: bool,
+}
+
 impl GpuDevice {
-    #[allow(clippy::too_many_arguments)]
     pub fn new(
         name: String,
         pci: PciDevice,
         render: u32,
         card: u32,
         default: Option<bool>,
         gpu_vendor: GpuVendor,
         nvidia_minor: Option<u32>,
-        discrete: bool,
-        vfio: bool,
-        available: bool,
-        virtual_gpu: bool,
+        flags: GpuFlags,
     ) -> GpuDevice {
         GpuDevice {
             name,
             pci,
             render,
             card,
             default,
             gpu_vendor,
             nvidia_minor,
-            discrete,
-            vfio,
-            available,
-            virtual_gpu,
+            discrete: flags.discrete,
+            vfio: flags.vfio,
+            available: flags.available,
+            virtual_gpu: flags.virtual_gpu,
         }
     }

Call sites in crates/cardwire-daemon/src/core/gpu/enumerator.rs then become explicit:

GpuFlags { discrete, available, ..Default::default() }
🤖 Prompt for 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.

In `@crates/cardwire-daemon/src/core/gpu/models.rs` around lines 139 - 166,
Replace the adjacent boolean parameters of GpuDevice::new with a named GpuFlags
struct containing discrete, vfio, available, and virtual_gpu, deriving or
implementing Default as appropriate. Remove the
#[allow(clippy::too_many_arguments)] attribute, update GpuDevice construction to
read the struct fields, and revise every enumerator call site to use explicit
GpuFlags values such as GpuFlags { discrete, available, ..Default::default() }.
crates/cardwire-daemon/src/interface/debug.rs (1)

115-127: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

This early return abandons the daemon mid-ritual.

By Line 124 the rebuild has already replaced *pci_list at Line 95, removed every GPU object from the object server, aborted every power task, and cleared gpu_interfaces. If GpuInterface::build fails here, refresh_gpu returns at once. The Hybrid fallback described in the comment at Lines 151 to 155 never runs, so the eBPF block map stays exactly as your own comment warns against: stale, with a GPU possibly still blocked. The published GPU API is also left partial, and no watcher exists for the devices that were dropped.

The condition then becomes permanent. *pci_list already holds the new list, so the next refresh_gpu finds no difference and skips the repair entirely. The same reasoning applies to the object_server.at(...).await? at Line 134.

Route both failures through the Hybrid fallback instead of returning directly. Extract the rebuild into a helper that returns a Result, then apply the existing fallback to its error.

🐛 Proposed shape
-            for (id, device) in new_gpu_list {
-                let gpu = GpuInterface::build(
-                    id as u32,
-                    device,
-                    Arc::clone(&self.blocker),
-                    Arc::clone(&self.pci_list),
-                    Arc::clone(&self.gpu_state),
-                    Arc::clone(&self.mode_state),
-                )
-                .map_err(|err| fdo::Error::Failed(err.to_string()))?;
-
-                gpu_interfaces.insert(id, gpu);
-            }
+            let mut build_error = None;
+            for (id, device) in new_gpu_list {
+                match GpuInterface::build(
+                    id as u32,
+                    device,
+                    Arc::clone(&self.blocker),
+                    Arc::clone(&self.pci_list),
+                    Arc::clone(&self.gpu_state),
+                    Arc::clone(&self.mode_state),
+                ) {
+                    Ok(gpu) => {
+                        gpu_interfaces.insert(id, gpu);
+                    }
+                    // Record and continue: an early return would leave the eBPF block map stale
+                    // with no hybrid fallback.
+                    Err(err) => {
+                        warn!("failed to build GPU interface {id}: {err}");
+                        build_error.get_or_insert(fdo::Error::Failed(err.to_string()));
+                    }
+                }
+            }

Then, after the locks are dropped, if build_error is set, apply effective_set_mode(Modes::Hybrid, true) before returning the error.

🤖 Prompt for 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.

In `@crates/cardwire-daemon/src/interface/debug.rs` around lines 115 - 127,
Refactor the rebuild logic in refresh_gpu into a helper returning a Result, and
route both GpuInterface::build failures and object_server.at failures into that
result instead of returning immediately. After releasing the relevant locks, if
the rebuild failed, invoke effective_set_mode with Hybrid and force enabled,
then return the original error. Ensure the fallback runs before any error return
so the daemon restores a consistent Hybrid state.
crates/cardwire-daemon/src/models.rs (1)

179-192: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Renew the NVIDIA inode map after GPU hotplug.

DebugInterface::refresh_gpu rebuilds GPU IDs, but it does not repopulate CW_EXP_BLK_INO. Since block_exp_inode only inserts entries, stale IDs and missing inodes persist. Clear CW_EXP_BLK_INO, then repopulate it from the refreshed GPU list.

🤖 Prompt for 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.

In `@crates/cardwire-daemon/src/models.rs` around lines 179 - 192, Update the GPU
hotplug refresh flow around DebugInterface::refresh_gpu to clear CW_EXP_BLK_INO
before rebuilding NVIDIA inode mappings, then repopulate it from the refreshed
GPU list using block_exp_inode. Ensure stale GPU IDs and missing inode entries
are removed while preserving the existing NVIDIA/non-default GPU filtering.
🤖 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 `@crates/cardwire-daemon/src/core/gpu/egl.rs`:
- Around line 54-64: Trim device_array to the count returned by the second
query_devices call before iterating over it, so query_device_str only receives
populated EGLDeviceEXT handles. Update the enumeration flow around query_devices
and the subsequent for loop while preserving the existing null string-pointer
handling.
- Around line 24-45: Update GpuEnumerator::build to load libEGL, resolve the
required query functions, enumerate EGL devices, and construct the
device-to-render-node map once alongside vlk_physical_devices. Store or pass
that map into build_gpu, and change build_gpu/is_discrete_egl to reuse it
instead of loading EGL, resolving symbols, or enumerating devices for each
graphics PCI device.

In `@crates/cardwire-daemon/src/core/gpu/enumerator.rs`:
- Around line 117-132: Guard the is_discrete_egl probe in the discrete-device
calculation with available, so it is skipped when drm_node_ids fails and render
is u32::MAX. Preserve the existing Vulkan check and EGL error handling for
devices with available set to true.

In `@crates/cardwire-daemon/src/core/gpu/helpers.rs`:
- Around line 107-113: Replace the blocking std::thread::sleep in drm_node_ids
with an async wait, or ensure the helper is only called from blocking workers;
wrap GpuEnumerator::build() and enumerate in
crates/cardwire-daemon/src/models.rs lines 54-55 with
tokio::task::spawn_blocking, and apply the same change in
DebugInterface::refresh_gpu in crates/cardwire-daemon/src/interface/debug.rs
while avoiding holding pci_list or gpu_list write locks across the blocking
task.

In `@crates/cardwire-daemon/src/core/gpu/models.rs`:
- Around line 130-137: The unused _vfio() accessor should be removed from the
model implementation. Delete the _vfio method while retaining the private vfio
field, and leave is_virtual() unchanged for check_default_drm_class().

In `@crates/cardwire-daemon/src/core/gpu/vulkan.rs`:
- Around line 40-59: Update the physical-device insertion logic in the Vulkan
enumeration loop to preserve the first device for each PCI ID instead of
overwriting it. Detect duplicate keys using the existing pci_id and emit a
warning containing the collision context, while leaving the original Arc entry
unchanged.

In `@crates/cardwire-daemon/src/interface/gpu.rs`:
- Around line 304-320: Update the D-Bus handler around
external_display_connected to execute the synchronous display-state check inside
tokio::task::spawn_blocking, matching the existing patterns in the sibling
callers. Await the blocking task and preserve the current Ok(true), Err, and
Ok(false) handling, including refusal to block when the check fails or detects a
connected display.

In `@crates/cardwire-daemon/src/interface/mode.rs`:
- Around line 248-294: In the mode-switch branch, collect the available GPU
entries once and reuse that collection for the exactly-two count, the
has_offload_dgpu topology check, and the offload-card lookup. Update the logic
around has_offload_dgpu and offload_card to eliminate repeated is_available()
filters while preserving the existing validation and disappearance error
behavior.
- Around line 332-340: Update the Modes::Hybrid branch to call unblock_gpu() for
every GPU in gpu_list.values_mut(), removing the is_available() filter. Preserve
the existing error propagation so Hybrid reliably clears stale block entries
even when device availability cannot be resolved.

In `@crates/cardwire-daemon/src/interface/switcheroo.rs`:
- Around line 136-137: Update the Environment insertion in the relevant D-Bus
property getter to handle OwnedValue::try_from(env_val) without unwrap: insert
the converted value only when conversion succeeds, and gracefully skip the
property on failure so the getter cannot panic.
- Around line 57-67: Update the vendor-routing match around GpuVendor::Nvidia to
also use PciDevice::driver() when selecting environment variables. Preserve the
existing proprietary NVIDIA variables for the NVIDIA driver, but emit DRI_PRIME
and a matching VK_LOADER_DRIVERS_SELECT glob such as *nouveau* for Mesa/NVK
devices so Vulkan selects the bound driver.

In `@nix/default.nix`:
- Around line 66-69: Update both GPU-ID substitutions in the Nix build
expression—covering the pci.ids replacement and the amdgpu.ids replacement—to
use --replace-fail instead of --replace-warn, while preserving their existing
paths and replacement values.

---

Outside diff comments:
In `@crates/cardwire-daemon/src/core/gpu/models.rs`:
- Around line 139-166: Replace the adjacent boolean parameters of GpuDevice::new
with a named GpuFlags struct containing discrete, vfio, available, and
virtual_gpu, deriving or implementing Default as appropriate. Remove the
#[allow(clippy::too_many_arguments)] attribute, update GpuDevice construction to
read the struct fields, and revise every enumerator call site to use explicit
GpuFlags values such as GpuFlags { discrete, available, ..Default::default() }.

In `@crates/cardwire-daemon/src/interface/debug.rs`:
- Around line 115-127: Refactor the rebuild logic in refresh_gpu into a helper
returning a Result, and route both GpuInterface::build failures and
object_server.at failures into that result instead of returning immediately.
After releasing the relevant locks, if the rebuild failed, invoke
effective_set_mode with Hybrid and force enabled, then return the original
error. Ensure the fallback runs before any error return so the daemon restores a
consistent Hybrid state.

In `@crates/cardwire-daemon/src/models.rs`:
- Around line 179-192: Update the GPU hotplug refresh flow around
DebugInterface::refresh_gpu to clear CW_EXP_BLK_INO before rebuilding NVIDIA
inode mappings, then repopulate it from the refreshed GPU list using
block_exp_inode. Ensure stale GPU IDs and missing inode entries are removed
while preserving the existing NVIDIA/non-default GPU filtering.
🪄 Autofix

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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9cdb8930-dedf-454d-a70f-5e78a0a60d69

📥 Commits

Reviewing files that changed from the base of the PR and between 23dd0ac and e65631f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • .github/workflows/cicd.yml
  • Cargo.toml
  • crates/cardwire-daemon/Cargo.toml
  • crates/cardwire-daemon/src/core/gpu/discover.rs
  • crates/cardwire-daemon/src/core/gpu/egl.rs
  • crates/cardwire-daemon/src/core/gpu/enumerator.rs
  • crates/cardwire-daemon/src/core/gpu/helpers.rs
  • crates/cardwire-daemon/src/core/gpu/mod.rs
  • crates/cardwire-daemon/src/core/gpu/models.rs
  • crates/cardwire-daemon/src/core/gpu/vulkan.rs
  • crates/cardwire-daemon/src/daemon.rs
  • crates/cardwire-daemon/src/interface/debug.rs
  • crates/cardwire-daemon/src/interface/gpu.rs
  • crates/cardwire-daemon/src/interface/mode.rs
  • crates/cardwire-daemon/src/interface/switcheroo.rs
  • crates/cardwire-daemon/src/models.rs
  • crates/cardwire-daemon/src/tasks/monitor_display.rs
  • flake.nix
  • nix/default.nix
💤 Files with no reviewable changes (1)
  • crates/cardwire-daemon/src/core/gpu/discover.rs

Comment thread crates/cardwire-daemon/src/core/gpu/egl.rs
Comment thread crates/cardwire-daemon/src/core/gpu/egl.rs
Comment thread crates/cardwire-daemon/src/core/gpu/enumerator.rs Outdated
Comment thread crates/cardwire-daemon/src/core/gpu/helpers.rs
Comment thread crates/cardwire-daemon/src/core/gpu/models.rs
Comment thread crates/cardwire-daemon/src/interface/mode.rs Outdated
Comment thread crates/cardwire-daemon/src/interface/mode.rs
Comment thread crates/cardwire-daemon/src/interface/switcheroo.rs
Comment thread crates/cardwire-daemon/src/interface/switcheroo.rs Outdated
Comment thread nix/default.nix Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/cardwire-daemon/src/interface/debug.rs (2)

175-183: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Return a D-Bus error when the Hybrid recovery spell fails.

If target reapplication fails and the forced Modes::Hybrid call also fails, this branch only logs the failures. refresh_gpu() then returns Ok(()). The caller cannot detect that GPU block state may remain stale.

Return fdo::Error::Failed when the fallback fails.

Proposed fix
                 if let Err(fb) = self
                     .mode_interface
                     .effective_set_mode(Modes::Hybrid, true)
                     .await
                 {
-                    warn!("failed to fall back to hybrid mode on hotplug: {fb}");
+                    let message =
+                        format!("failed to re-apply mode on hotplug: {e}; hybrid fallback failed: {fb}");
+                    error!("{message}");
+                    return Err(fdo::Error::Failed(message));
                 }
🤖 Prompt for 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.

In `@crates/cardwire-daemon/src/interface/debug.rs` around lines 175 - 183, Update
the hotplug recovery branch in refresh_gpu so that when the fallback
effective_set_mode(Modes::Hybrid, true) call fails, it returns
fdo::Error::Failed instead of only logging and continuing with Ok(()). Preserve
both existing warnings and the successful fallback path.

158-175: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Cast hotplug reapplication from the requested mode under one transition.

current_mode_value() returns the temporary effective mode. set_requested_mode() can persist Integrated while a display override sets the effective mode to Hybrid. This path then resolves Hybrid again and does not restore Integrated when the override no longer applies.

This path also resolves the target before effective_set_mode() locks transition. If set_requested_mode() completes during that gap, the forced refresh can overwrite the newer effective mode.

Add one ModeInterface method that locks transition, reads requested_mode_value(), resolves the target, and applies it before releasing the lock. Call that method here.

🤖 Prompt for 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.

In `@crates/cardwire-daemon/src/interface/debug.rs` around lines 158 - 175,
Replace the current hotplug sequence in the relevant debug handler with a new
ModeInterface method that holds transition across reading
requested_mode_value(), resolving the display target, and applying it via
effective_set_mode(). Preserve the existing fallback-to-Hybrid error behavior
within that method, then call it from this path so concurrent
set_requested_mode() updates cannot be overwritten.
🤖 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.

Outside diff comments:
In `@crates/cardwire-daemon/src/interface/debug.rs`:
- Around line 175-183: Update the hotplug recovery branch in refresh_gpu so that
when the fallback effective_set_mode(Modes::Hybrid, true) call fails, it returns
fdo::Error::Failed instead of only logging and continuing with Ok(()). Preserve
both existing warnings and the successful fallback path.
- Around line 158-175: Replace the current hotplug sequence in the relevant
debug handler with a new ModeInterface method that holds transition across
reading requested_mode_value(), resolving the display target, and applying it
via effective_set_mode(). Preserve the existing fallback-to-Hybrid error
behavior within that method, then call it from this path so concurrent
set_requested_mode() updates cannot be overwritten.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 72961942-10c6-47f8-8d65-1c2084872c5e

📥 Commits

Reviewing files that changed from the base of the PR and between e65631f and 0753abe.

📒 Files selected for processing (5)
  • crates/cardwire-daemon/src/core/gpu/egl.rs
  • crates/cardwire-daemon/src/core/gpu/enumerator.rs
  • crates/cardwire-daemon/src/interface/debug.rs
  • crates/cardwire-daemon/src/interface/mode.rs
  • crates/cardwire-daemon/src/interface/switcheroo.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 `@nix/default.nix`:
- Line 66: Update the substituteInPlace target in the Nix postPatch
configuration to reference the existing pci_device.rs file under
crates/cardwire-daemon/src/core/pci instead of the absent discover.rs path,
while preserving the pci.ids replacement.
🪄 Autofix

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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1fb15ffb-09d1-47ef-98f1-a8430172d789

📥 Commits

Reviewing files that changed from the base of the PR and between 0753abe and 502c77b.

📒 Files selected for processing (1)
  • nix/default.nix

Comment thread nix/default.nix

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/cardwire-daemon/src/core/gpu/enumerator.rs (1)

97-109: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the u32::MAX sentinel rune out of user-facing node data.

The VFIO path and the drm_node_ids error path store u32::MAX in card and render while setting available to false. The GUI still renders these fields as card4294967295 / renderD4294967295 for unavailable GPUs. This is an invalid device description.

Use optional node IDs, or render Unavailable whenever available is false.

Also applies to: 118-123, 138-150

🤖 Prompt for 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.

In `@crates/cardwire-daemon/src/core/gpu/enumerator.rs` around lines 97 - 109,
Remove the u32::MAX sentinel from unavailable GPU node data in the
GpuDevice::new calls covering the VFIO and drm_node_ids error paths, using
optional absent node IDs or the established unavailable representation instead.
Ensure unavailable devices cannot render card4294967295 or renderD4294967295,
while preserving valid node IDs for available GPUs.
🤖 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.

Outside diff comments:
In `@crates/cardwire-daemon/src/core/gpu/enumerator.rs`:
- Around line 97-109: Remove the u32::MAX sentinel from unavailable GPU node
data in the GpuDevice::new calls covering the VFIO and drm_node_ids error paths,
using optional absent node IDs or the established unavailable representation
instead. Ensure unavailable devices cannot render card4294967295 or
renderD4294967295, while preserving valid node IDs for available GPUs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 77117cc2-8670-4036-aea8-f46509a0a40f

📥 Commits

Reviewing files that changed from the base of the PR and between bfe6cb2 and 4cda388.

📒 Files selected for processing (3)
  • crates/cardwire-daemon/src/core/gpu/enumerator.rs
  • crates/cardwire-gui/src/app.rs
  • crates/cardwire-gui/src/ui.rs
💤 Files with no reviewable changes (1)
  • crates/cardwire-gui/src/app.rs

@luytan
luytan merged commit df02ca6 into main Aug 5, 2026
7 checks passed
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.

switcheroo shim incorrectly populates Discrete key

1 participant