feat(presenter): DRM card selection + a usable kmsdrm swapchain error
ci / web (push) Successful in 1m2s
ci / docs-site (push) Successful in 1m15s
apple / swift (push) Successful in 5m25s
ci / bench (push) Successful in 7m44s
deb / build-publish-host (push) Successful in 10m30s
decky / build-publish (push) Successful in 30s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
android / android (push) Successful in 12m52s
deb / build-publish (push) Successful in 11m46s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 11s
arch / build-publish (push) Successful in 13m6s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 45s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 12s
ci / rust-arm64 (push) Successful in 13m32s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m1s
deb / build-publish-client-arm64 (push) Successful in 9m12s
windows-host / package (push) Successful in 18m4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 7m1s
flatpak / build-publish (push) Successful in 7m25s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m46s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Failing after 9m7s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 9m24s
ci / rust (push) Successful in 23m48s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m28s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m46s
docker / deploy-docs (push) Successful in 22s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m42s
release / apple (push) Successful in 31m30s
docker / build-push-arm64cross (push) Successful in 7m6s
apple / screenshots (push) Successful in 23m4s
ci / web (push) Successful in 1m2s
ci / docs-site (push) Successful in 1m15s
apple / swift (push) Successful in 5m25s
ci / bench (push) Successful in 7m44s
deb / build-publish-host (push) Successful in 10m30s
decky / build-publish (push) Successful in 30s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
android / android (push) Successful in 12m52s
deb / build-publish (push) Successful in 11m46s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 11s
arch / build-publish (push) Successful in 13m6s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 45s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 12s
ci / rust-arm64 (push) Successful in 13m32s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m1s
deb / build-publish-client-arm64 (push) Successful in 9m12s
windows-host / package (push) Successful in 18m4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 7m1s
flatpak / build-publish (push) Successful in 7m25s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m46s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Failing after 9m7s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 9m24s
ci / rust (push) Successful in 23m48s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m28s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 4m46s
docker / deploy-docs (push) Successful in 22s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m42s
release / apple (push) Successful in 31m30s
docker / build-push-arm64cross (push) Successful in 7m6s
apple / screenshots (push) Successful in 23m4s
Both come straight out of the first compositor-less bring-up (P3), on a two-GPU box: PUNKTFUNK_DRM_CARD=<n> pins SDL's KMSDRM device index. SDL enumerates /dev/dri/card* and takes the first it can open, which is regularly the wrong one: it chose the card a live compositor already held DRM master on and died at swapchain creation, while the idle card with the connected display sat unused. Kept an explicit operator choice rather than auto-detection — deciding "is this card already mastered" needs the very ioctl that taking master IS, so any in-process guess would be fragile. The swapchain error now carries what to actually check. "vkCreateSwapchainKHR: Initialization of an object has failed" is useless to someone bringing up a kiosk; on the kmsdrm backend it now names the pinned card and lists the three real causes in order (no connected connector / another DRM master / NVIDIA). Empty on every other backend, where it would be noise. The NVIDIA note is measured, not guessed: on NVIDIA proprietary + kmsdrm, Vulkan enumerates the GPU AND the display (VK_KHR_display reports the connected HDMI connector) and still fails — as root, with nvidia_drm.modeset=Y, on a card no compositor was using. Not permissions, not DRM master; their direct-display path wants the display leased via vkAcquireDrmDisplayEXT and SDL's kmsdrm surface path does not do that. Plan: punktfunk-planning design/embedded-arm64-client.md §P3 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -360,6 +360,28 @@ fn run_inner(mut opts: SessionOpts, mut mode: ModeCtl) -> Result<Option<Outcome>
|
|||||||
// identity and the session window gets the default-Wayland icon (the Linux analog of
|
// identity and the session window gets the default-Wayland icon (the Linux analog of
|
||||||
// the AppUserModelID adoption above).
|
// the AppUserModelID adoption above).
|
||||||
sdl3::hint::set("SDL_APP_ID", "io.unom.Punktfunk");
|
sdl3::hint::set("SDL_APP_ID", "io.unom.Punktfunk");
|
||||||
|
// `PUNKTFUNK_DRM_CARD=<n>` → SDL's KMSDRM device index, for a compositor-less (kiosk/embedded)
|
||||||
|
// run. SDL enumerates /dev/dri/card* and takes the first one it can open, which on a
|
||||||
|
// multi-GPU box is regularly the WRONG one: measured on a two-card machine it chose the card
|
||||||
|
// a live compositor already held DRM master on and failed at swapchain creation, while the
|
||||||
|
// idle card with the connected display sat unused. There is no reliable way to pick from
|
||||||
|
// inside the process (detecting "already mastered" needs the ioctl that taking master IS), so
|
||||||
|
// this stays an explicit operator choice rather than fragile auto-detection.
|
||||||
|
// Ignored unless the kmsdrm backend is actually in use.
|
||||||
|
if let Ok(card) = std::env::var("PUNKTFUNK_DRM_CARD") {
|
||||||
|
if card.chars().all(|c| c.is_ascii_digit()) && !card.is_empty() {
|
||||||
|
tracing::info!(
|
||||||
|
card,
|
||||||
|
"PUNKTFUNK_DRM_CARD: pinning SDL's KMSDRM device index"
|
||||||
|
);
|
||||||
|
sdl3::hint::set("SDL_KMSDRM_DEVICE_INDEX", &card);
|
||||||
|
} else {
|
||||||
|
tracing::warn!(
|
||||||
|
card,
|
||||||
|
"PUNKTFUNK_DRM_CARD must be a card NUMBER (e.g. 0) — ignoring"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
let sdl = sdl3::init().context("SDL init")?;
|
let sdl = sdl3::init().context("SDL init")?;
|
||||||
let video = sdl.video().context("SDL video")?;
|
let video = sdl.video().context("SDL video")?;
|
||||||
let events = sdl.event().context("SDL events")?;
|
let events = sdl.event().context("SDL events")?;
|
||||||
|
|||||||
@@ -3,9 +3,34 @@
|
|||||||
use super::setup::pick_formats;
|
use super::setup::pick_formats;
|
||||||
use super::{OverlayPipe, Presenter};
|
use super::{OverlayPipe, Presenter};
|
||||||
use crate::csc::CscPass;
|
use crate::csc::CscPass;
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
|
|
||||||
|
/// Extra guidance appended to a swapchain-creation failure when SDL is on the KMSDRM backend —
|
||||||
|
/// i.e. a compositor-less kiosk/embedded run, where the bare Vulkan error is close to useless.
|
||||||
|
///
|
||||||
|
/// Measured on an NVIDIA proprietary + KMSDRM box: SDL opens the card, Vulkan enumerates the GPU
|
||||||
|
/// *and* the display (`VK_KHR_display` reports the connected HDMI connector), then
|
||||||
|
/// `vkCreateSwapchainKHR` returns ERROR_INITIALIZATION_FAILED — as root, with `nvidia_drm.modeset`
|
||||||
|
/// on, on a card no compositor was using. So it is neither permissions nor DRM master: NVIDIA's
|
||||||
|
/// direct-to-display path wants the display leased (`vkAcquireDrmDisplayEXT`) and SDL's KMSDRM
|
||||||
|
/// surface path does not do that for it. Empty on every other backend, where the message would be
|
||||||
|
/// noise.
|
||||||
|
fn kmsdrm_swapchain_hint() -> String {
|
||||||
|
let kmsdrm = std::env::var("SDL_VIDEODRIVER").is_ok_and(|v| v.eq_ignore_ascii_case("kmsdrm"));
|
||||||
|
if !kmsdrm {
|
||||||
|
return String::new();
|
||||||
|
}
|
||||||
|
let card = std::env::var("PUNKTFUNK_DRM_CARD").unwrap_or_else(|_| "unset".into());
|
||||||
|
format!(
|
||||||
|
" — under SDL_VIDEODRIVER=kmsdrm (PUNKTFUNK_DRM_CARD={card}). Check, in order: the card \
|
||||||
|
has a CONNECTED connector (`cat /sys/class/drm/card*-*/status`); nothing else holds DRM \
|
||||||
|
master on it (a running compositor does — pin another card with PUNKTFUNK_DRM_CARD=<n>); \
|
||||||
|
and the driver is Mesa. NVIDIA's proprietary direct-display path is known to fail here \
|
||||||
|
even as root with a display Vulkan can enumerate."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl Presenter {
|
impl Presenter {
|
||||||
/// (Re)build the swapchain for the window's current pixel size. Also the resize path.
|
/// (Re)build the swapchain for the window's current pixel size. Also the resize path.
|
||||||
pub fn recreate_swapchain(&mut self, window: &sdl3::video::Window) -> Result<()> {
|
pub fn recreate_swapchain(&mut self, window: &sdl3::video::Window) -> Result<()> {
|
||||||
@@ -66,8 +91,8 @@ impl Presenter {
|
|||||||
.present_mode(self.present_mode)
|
.present_mode(self.present_mode)
|
||||||
.clipped(true)
|
.clipped(true)
|
||||||
.old_swapchain(old);
|
.old_swapchain(old);
|
||||||
let swapchain =
|
let swapchain = unsafe { self.swap_d.create_swapchain(&info, None) }
|
||||||
unsafe { self.swap_d.create_swapchain(&info, None) }.context("vkCreateSwapchainKHR")?;
|
.map_err(|e| anyhow!("vkCreateSwapchainKHR: {e}{}", kmsdrm_swapchain_hint()))?;
|
||||||
// The old swapchain and everything tied to its images dies NOW: the fence
|
// The old swapchain and everything tied to its images dies NOW: the fence
|
||||||
// quiesce covered our own command buffers, the queue drain above covered the
|
// quiesce covered our own command buffers, the queue drain above covered the
|
||||||
// presentation engine's semaphore waits — nothing can still reference them.
|
// presentation engine's semaphore waits — nothing can still reference them.
|
||||||
|
|||||||
Reference in New Issue
Block a user