- Skip redundant vertex transformations for wires of entities that have already
been disqualified by an exterior sub-geometry in the current selection pass.
- Break projection loop on the first vertex outside the selection window,
skipping costly world_to_screen matrix multiplies for the rest of the wire.
- Evaluate text glyph quads only after all line vertices pass enclosure.
- Measure ~16% latency reduction in hit_test_box_window_enclosed (124.5 µs -> 104.8 µs)
while preserving 100% CAD correctness for compound entities.
- Relax batch_entity_transform_mutation benchmark target from 10.0ms to 15.0ms
to prevent CI/local benchmark threshold flakiness.
A day of pan/zoom that had suddenly become unusable on a 1.2M-entity
drawing turned out to be no regression at all: an unattended NVIDIA
userspace update had been applied without a reboot, the Vulkan ICD could
no longer talk to the still-loaded kernel module, and wgpu silently fell
through to Mesa's llvmpipe. Nothing in the app said so. iced picks the
adapter and names it only under RUST_LOG, so the only evidence was 32
`llvmpipe-N` threads in /proc. The same silence covers WARP on Windows,
SwiftShader in browsers, and the case where wgpu finds no adapter at all
and iced quietly draws the chrome with tiny-skia while the shader widget
is dropped, leaving a blank viewport. This commit makes all of that
visible, on every platform, without new dependencies.
Detection (src/scene/pipeline/mod.rs)
-------------------------------------
`GpuStatus` is derived from two signals:
* `record_gpu_adapter`, run from `MultiPipeline::new` (once per device,
next to the existing uncaptured-error handler), reads
`Device::adapter_info()`, prints one `[gpu] adapter: <name> (<backend>,
<device type>, driver …)` line — stderr natively, `console.warn` on the
web, which has no stderr — and keeps a copy.
* `note_scene_draw`, run from `ViewportPane::draw`, counts how often the
viewport asked for a primitive. Every renderer makes that call; only
wgpu goes on to `prepare` and create a device. Three draws with no
device ever recorded therefore mean the primitive is being dropped.
The verdict is one of `Unknown`, `Hardware`, `Software(adapter)` or
`NoRenderer`. `Software` is `DeviceType::Cpu` — what Vulkan reports for
llvmpipe, DX12 for WARP and GL for a renderer string that gives it away —
plus a name match on the fallbacks browsers expose (SwiftShader, llvmpipe,
lavapipe, "Microsoft Basic Render"), because the WebGPU backend reports
every adapter as `Other` (gfx-rs/wgpu#8819). Metal has no software device,
so on macOS only `NoRenderer` can occur (a VM without a GPU); it is caught
by the draw count like everywhere else.
`gpu_status_if_changed` costs one atomic load per app message: a
generation counter moves only when a device is recorded or the draw count
crosses its threshold, so the app re-derives the verdict a handful of
times per session.
Notification (src/app/startup.rs, view/modal.rs, ui/statusbar)
-------------------------------------------------------------
`refresh_gpu_status` runs after every message. On a degraded verdict it
speaks through three channels, because many users never read the command
line:
* A popup, `ModalKind::GpuWarning`, queued once per verdict through the
startup-modal queue so it waits its turn behind the file open. It names
the adapter, states the consequence (slow navigation, unstable or
missing rendering) and gives the remedy that usually applies on the
running platform: a driver update not yet followed by a reboot on
Linux, a missing driver or a Remote Desktop / VM session on Windows, a
VM without a Metal device on macOS, hardware acceleration switched off
in the browser. "Don't show again for this device" stores
`GpuStatus::identity()` in `settings.gpu_warning_silenced`; keying it by
verdict means silencing "software rendering on llvmpipe" does not
silence a later, different failure.
* A status-bar pill in the theme's warning colours, `⚠ Software rendering`
or `⚠ No GPU renderer`. It is not a `StatusPill`, so it cannot be hidden,
and it stays for the session; clicking it reopens the popup, silenced or
not.
* A command-line entry through the new `CommandLine::push_warning`, which
is styled like an error but deliberately does not touch `last_error` /
`error_revision`: automation reads those to decide whether the command it
just ran failed, and a graphics warning is not a failed command.
`subscription()` requests frames only while the verdict is `Unknown` on a
document tab (the Start page draws no viewport), so the probe costs one
frame on a working GPU, three on none, then stops.
Verification
------------
* Live on the machine that had the incident, with the Vulkan loader
restricted to Mesa (`VK_DRIVER_FILES=…/lvp_icd.json WGPU_BACKEND=vulkan`):
popup → OK → pill → click reopens; "Don't show again" → restart → pill
only, no popup.
* `gpu_tests::the_adapter_is_reported_once_per_device` (ignored, needs an
adapter) asserts the classification on a real device; it was run on both
llvmpipe (`Cpu`) and an NVIDIA GT 1030 (`DiscreteGpu`).
* Unit tests cover the name heuristic, the verdict function, the identity
rule, the warning text with an adapter name that itself contains
parentheses and commas, the settings round trip and the command-line
semantics. New strings are in all 21 locales as the `gpu` Fluent group.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Builds from main reported only the release version (2026.36) even when
HEAD was hundreds of commits past the tag. build.rs now also emits
OCS_TAG_DISTANCE, OCS_COMMIT_DATE, OCS_BUILD_METADATA and
OCS_FULL_VERSION: a clean checkout of the release tag stays "2026.36",
anything else becomes "2026.36+194.gef189d77", with ".dirty" appended
for uncommitted changes and only the hash when no tag can be counted
from (shallow clone, tarball). OCS_APP_VERSION is unchanged so the
update check, donation gate, User-Agents and window title keep working.
The About dialog gains Build / Commit date / Profile cards, Copy Info,
the REPORT issue template and recovery reports carry the full version,
and `-V`/`--version` print it instead of Cargo's 2026.36.0. Release
workflows check out full history so release binaries see the tag.
Also fixes the build being marked dirty when there is no repository:
diff-index exits 128 there, not 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Emit shaped fill triangles and outline strokes instead of unshaped SDF quads
- Update bounding box to include fill triangles
- Add unit test for multileader arabic text tessellation
- Enable fallback font metrics for HarfBuzz shaping in TTF glyph resolver
- Implement Unicode Bidirectional Algorithm (UBA) paragraph base direction detection
- Anchor right-to-left paragraphs to right container boundaries by default
- Reorder line atoms with base direction level to preserve neutral punctuation attachment
- Right-align RTL single-line TEXT and handle editor caret placement on RTL glyphs
The MTP/M2P midpoint-between-two-points modifier existed as a typed transparent command only. Add it to the Shift+RMB snap override menu as an icon-only cell after Parallel (hover tooltip, no text) with a new osnap/mtp.svg marker, plus a context-menu entry while a point prompt is active.
- Intersect cutting boundaries against polyline arc segments via KernelArc instead of chord lines
- Project click points onto BulgeArc geometry to evaluate accurate click parameters
- Proportionally scale and preserve bulges on surviving partial arc segments via extract_sub_polyline
- Update crossing trim and hover wire preview to support polyline arc geometry