A PC whose GPUs were installed from different driver packages of one vendor
(an ASUS G513QY: the integrated Radeon on the 2021 package, the RX 6800M on a
2026 one) gives the Vulkan loader two versions of that vendor's driver, and
the RX 6800M is listed once through each. wgpu's request_adapter takes the
first discrete entry, the 2021 driver's, and the window drawn through it dies
inside amdvlk64.dll (0xc0000005) before it paints: the main thread is in the
2021 driver, the fault on a thread of the 2026 one, no Rust frame anywhere.
Left to the #820 ladder, two crashed launches end in DirectX 12 only.
filmcraft_gpu::adapter::choose is wgpu's own rule (the adapters that can draw
to the surface, by device type for WGPU_POWER_PREF, then as enumerated) with
one addition: when the adapter that rule picks is listed again by the same
driver (same backend, device type, PCI vendor and device and driver name; the
same bus id when both report one), the entry exposing the most wgpu features
is used, i.e. the newer version (68 against 60 here, a strict superset).
Without such a duplicate the choice is wgpu's own, and entries of one GPU from
different drivers (RADV and AMDVLK, a vendor's driver and Dozen) are left
alone.
graphics::apply installs it as egui-wgpu's native_adapter_selector in the
default mode on Windows, as #609 was asked to be rebased onto the #827
start-up. DirectX 12 only, software mode and the other platforms keep their
adapter choice, and the ladder is untouched. The adapters and the choice go
to the log.
Tests: the laptop's listing (wgpu's rule picks the entry that crashed, choose
the other one), listings without two versions of one driver over every power
preference (choose equals wgpu's rule), surface, bus id and driver cases, and
one against the adapters of the machine running the tests (the rule is what
request_adapter returns there).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#827 rewrote apps/filmcraft/src/graphics.rs and the start-up in main.rs, so
the selector of this branch no longer applies. main's tree is taken whole
(graphics.rs, main.rs and the README note about WGPU_ADAPTER_NAME included);
the next commit hooks the selector into the GraphicsStart flow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Effects panel drew a folder's name from the last `/`-separated part of
its key (`<top>/<folder>`), so Audio Effects ▸ Noise Reduction/Restoration
showed as "Restoration" — truncated in English and untranslated in every
other language, since the catalogs translate the full name. Split once,
after the top folder, instead.
Test: effects_panel_lists_the_premiere_transition_folders checks the label.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013XWUReif4gqcjU5GsjYioc
The Ruler walked one grid (the tick stride) and labelled a frame only when it
was also a multiple of the label stride. Chosen independently, the two need not
divide: at 24 fps and 100 pt a second, 5-frame ticks against 48-frame labels
meet only every lcm(5, 48) = 240 frames — one timestamp per 10 s, the rest
returning at the next zoom step, where the ladder happens to divide.
Take the tick stride as the finest division of the label stride that still
leaves ticks 12 pt apart, so both are one grid. Every label then falls on a
tick and can no longer be skipped, and the ticks stay evenly spaced around it
instead of bunching either side. Labels are >= 110 pt apart, so a divisor of at
most 9 always meets the minimum. The labelled stride, and so every timecode and
the frame it is drawn at, is unchanged.
Strides are clamped to >= 1 (the loop advances by one and tests the other for
divisibility), and the ladder's products saturate, as FrameRate is plain serde
data from the project file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>