diff --git a/README.md b/README.md index 50c2e46..25a2a25 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,10 @@ Options: `--server`, `--port`, `--multicast`, `--interface`, `--buffer-mb` window the toolbar offers, default 600), `--holdoff` (default trigger hold-off), `--recv-buffer`, `--plots` (the number of stacked plots to start with, 1..8; rows are added and removed from the **Layout editor** afterwards), `--relative-time` (see below), -`--capture-packets N` (see "Packet capture" below). +`--capture-packets N` (see "Packet capture" below), `--renderer NAME` (GTK's renderer: +`cairo`, `gl` or `vulkan`; `cairo` is the software preset that exports both +`GSK_RENDERER` and `LIBGL_ALWAYS_SOFTWARE` before GTK initialises — see the SSH +limitation below). **What a large `--buffer-mb` costs at startup, and what the window says while it happens.** The buffer's rings are value-initialised, so allocating one touches every byte of `--buffer-mb` --- about @@ -2922,6 +2925,29 @@ silence timeout and then resumes, which is the EMI case end to end. inner popover takes the grab, the menu closes under the click, and the bubble is left parented to a row in a hidden popover. Export is described in the Documentation instead. +- **Over SSH with X11 forwarding the window may never paint or allocate, and the cure is + `--renderer cairo`.** There are two walls, in order. + GTK4 defaults to Vulkan and a forwarded display has no usable Vulkan device, so the + log shows `vkCreateSwapchainKHR(): Initialization of an object could not be completed + ... (VK_ERROR_INITIALIZATION_FAILED)` and nothing is drawn. `GSK_RENDERER` alone gets + past that, but the plot is a `GtkGLArea` and asks GDK for a GL context of its own + whatever GSK draws with; over forwarding there is no hardware one, and the window then + stays unallocated — `header: window_button_w=0` with a zero `bar_w`, and no + `[width-probe]` line (it prints only once the plot has a viewport wider than 100 px) — + while the stream side is fine. Absence of `buffer allocated:` is *not* part of that + reading: signals arrive disabled, so a start with nothing enabled plans no buffer and + prints no line even on a healthy display, and the plot stays empty until a signal's + switch is turned on. + `--renderer cairo` is the preset for exactly this display: it exports + `GSK_RENDERER=cairo` **and** `LIBGL_ALWAYS_SOFTWARE=1` (llvmpipe answers the GL area), + the same pair `tools/make-help-shots.sh` uses. `--renderer gl` is the faster + alternative when indirect GL works. The variables are exported in `main()` **before** + `gtk_init()`/`adw_init()`, which is the whole trap: they are read when the renderer and + the GL context are created, so setting them later is a silent no-op. Without the option + nothing is set and GTK's own choice stands. No `GDK_BACKEND` companion exists on + purpose: an X11-forwarded session carries `DISPLAY` and no `WAYLAND_DISPLAY`, so GDK + already selects X11; forcing it would override a working local Wayland session for no + gain (if both are set and Wayland is broken, export `GDK_BACKEND=x11` yourself). - **The tour card can cover its own target, and in the documented headless run it does.** The card is placed under, over or beside the hole whenever one of those has room; when the target covers the whole content -- which is what the plot does at the diff --git a/docs/claude/build-and-run.md b/docs/claude/build-and-run.md index f52482d..5e23f4a 100644 --- a/docs/claude/build-and-run.md +++ b/docs/claude/build-and-run.md @@ -103,6 +103,12 @@ count to *start* with, 1..`RowSet::kMaxRows`; the layout editor owns it afterwar the value is required, and the popover button uses 1000 when the flag was absent). The flag is **not** persisted in the session file: persistence of the enable state belongs with `todo.md` item 3, alongside the relative-time toggle. +`--renderer cairo|gl|vulkan` exports `GSK_RENDERER` **at the top of `main()`** — +before `choose_source()`'s `adw_init()` and `run_ui()`'s, because GSK reads the variable when +it creates its renderer and setting it later is a silent no-op. `cairo` is the software preset +and also exports `LIBGL_ALWAYS_SOFTWARE=1`: the plot is a `GtkGLArea`, so a display with no +usable GPU fails twice, and this is the pair `tools/make-help-shots.sh` already sets. Nothing +is exported when the option is absent, so GTK's own choice stands. `tools/mock_udpstreamer.py` has waveform-injection options used by the probes: diff --git a/docs/claude/probe-readings.md b/docs/claude/probe-readings.md index 777032d..0f20a67 100644 --- a/docs/claude/probe-readings.md +++ b/docs/claude/probe-readings.md @@ -450,11 +450,25 @@ Two lines of the self-check output are assertions in disguise: floor are the two refusals, and because the probe emits `::clicked` straight at the widget it reaches the handler even when the button is insensitive, so `add above 0` there must leave `rows=` and `traces=` untouched rather than growing a ninth row. -- `header: window_button_w=W bar_w=B` (stderr, once a second) — **`W` must be +- `[udpscope] header: window_button_w=W bar_w=B` (stderr, once a second, **only when + `UDPSCOPE_PROBE_HEADER` is set**) — **`W` must be a small constant and must not track `B`**: the window control is a header-bar child, and an expanding one is measured into the title's centre box, so it stretched from the label to the title (516 px at a 1270 px bar before the fix, 121 px after). A `W` that - grows with the window width is that bug back. + grows with the window width is that bug back. The widths are still sampled every second + in every run, because the exit dump's `header:` line reads them; only the line is behind + the variable, since a reading printed on every run is a log flood (the `[notrace]` rule). + An unallocated window reads `0` for both, which is the reading for "the toplevel never + got a size", not for a layout drift. +- `[renderer] option=NAME GSK_RENDERER=V LIBGL_ALWAYS_SOFTWARE=V` (stderr, at startup, + before GTK initialises; only when `UDPSCOPE_PROBE_RENDERER` is set) — what `--renderer` + exported, printed from `main()` so the reading is of the environment a pre-init GTK will + see. `option=(unset)` with both variables `(unset)` is the default and the assertion that + the flag changes nothing unless asked. +- `[renderer] realized=TYPE` (stderr, once, from the first tick that finds a window renderer; + same probe) — the class GTK actually built (`GskCairoRenderer` for `--renderer cairo`), + which is the half of the claim the export alone cannot make. It is null until the window is + realized, so a tick that finds none tries again rather than printing a false reading. - `toolbar icons: trigger=NAME(ok) events=... pause=... cursors=... sidebar=... menu=...` (in the exit dump, beside `panel controls:`) — the icon name every toolbar button carries, with `gtk_icon_theme_has_icon()`'s verdict. **Every entry must read `(ok)`.** Three diff --git a/docs/claude/verification.md b/docs/claude/verification.md index e91556e..87525f4 100644 --- a/docs/claude/verification.md +++ b/docs/claude/verification.md @@ -155,6 +155,17 @@ precedent, not an exception. (the frame's ink is the image) and would otherwise be counted as well. The counter itself is in every build; see [probe-readings.md](probe-readings.md). +- `UDPSCOPE_PROBE_HEADER` (no value; **not** a scheduled probe) — the once-a-second + `[udpscope] header: window_button_w=… bar_w=…` line. The widths are sampled either way (the + exit dump reads them); the line is behind this variable because it is detail behind that + reading, not a line every run should print. An unallocated window reads `0` for both. +- `UDPSCOPE_PROBE_RENDERER` (no value; **not** a scheduled probe) — the renderer pair + `--renderer` should have exported, printed from `main()` before GTK initialises + (`[renderer] option=… GSK_RENDERER=… LIBGL_ALWAYS_SOFTWARE=…`), then the renderer GTK + built (`[renderer] realized=…`) once the window has one. Run it with `--renderer cairo` to + check the option reaches the environment before `gtk_init()`/`adw_init()`; run it without + the option to check the default is untouched. + - `UDPSCOPE_PROBE_TRIGRENUM_MS` — the trigger's capture must survive a **renumbering of its own channel**. It sets the reported geometry through the real widgets (1 s window, 10 % pre share, hold-off from `--holdoff`) on the mock's *square* alone, polls for a capture to open, and then diff --git a/help/C/org.udpscope.app/overview.page b/help/C/org.udpscope.app/overview.page index 6ea5685..43c2d4d 100644 --- a/help/C/org.udpscope.app/overview.page +++ b/help/C/org.udpscope.app/overview.page @@ -55,7 +55,20 @@
The signals arrive disabled. Nothing is acquired until you turn one on in the signal list, and enabling is what starts acquisition — so a freshly opened scope with an - idle side panel is working correctly, not failing to connect.
+ idle side panel is working correctly, not failing to connect. For the same reason a start + that has enabled nothing prints nobuffer allocated: line on stderr.
+
+ Over SSH with X11 forwarding the window may never appear, and the cure is
+ vkCreateSwapchainKHR() ... VK_ERROR_INITIALIZATION_FAILED warning is
+ GTK's default Vulkan renderer failing to make a swapchain and is what the option's
+ GSK_RENDERER=cairo answers; a window whose allocation stays zero
+ (window_button_w=0) means the plot's GL area has no context either, and
+ that is what its LIBGL_ALWAYS_SOFTWARE=1 answers. Both variables are
+ exported before GTK initialises, because they are read when the renderer and the GL
+ context are created.
See Choosing a source for the chooser and the multicast
diff --git a/src/config.cpp b/src/config.cpp
index 66c1a72..1b71402 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -62,6 +62,12 @@ void print_usage(std::FILE* out, const char* argv0) {
" time is then kept for export only\n"
" --open FILE open an exported waveform (.npy) instead of a live source:\n"
" signals from its columns, no socket, no acquisition\n"
+ " --renderer NAME render with cairo (software), gl or vulkan. cairo also\n"
+ " exports LIBGL_ALWAYS_SOFTWARE=1, because the plot is a\n"
+ " GtkGLArea and still needs a GL context. Over SSH/X11\n"
+ " forwarding use cairo: Vulkan cannot make a swapchain there\n"
+ " and the GL area has no hardware context, so the window\n"
+ " never paints or allocates\n"
" -h, --help this text\n",
argv0);
}
@@ -113,6 +119,15 @@ Config parse_args(int argc, char** argv) {
c.capture_packets = static_cast