Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc39146d0d | ||
|
|
147fc113c3 |
No files matched your search
+1
-1
@@ -4,7 +4,7 @@ VectorCraft is a clean-room, open-source, pure-Rust reimplementation of the Adob
|
||||
|
||||
This file tracks **how far we are and what's left**. Time estimates are wall-clock hours of continuous Claude Opus 5.5 agent work (including builds and the CI gate), given both for **one agent** and for **4–6 parallel agents** on disjoint crates. They are counted from the remaining work (see [Parity estimate](#parity-estimate)), calibrated against measured throughput, and updated as work lands.
|
||||
|
||||
_Last updated: 2026-10-09 (after placed documents (File › Place of a `.vectorcraft` file, linked), M4.14–M4.98, M8.1–M8.20, M14.4–M14.7, the MCP prompts, resource templates, completions and logging, the 2026-10-07 issue fixes, the Layers panel rework and the 2026-10-08 community contributions: Pen and shape-tool modifiers, path editing, Layers, Artboards, Swatches/Brushes and dialogs, Japanese composition and PDF text, Hebrew/Arabic type, interface languages (Spanish among them), the font menu, saved selections, MCP and EPS, the missing-font export warning (#319), open paths in the Shape Builder (#374), PDF/`.ai` import keeping strokes live and hidden and nested layers (#372), per-corner Live Corners (#381), SVG type opening at the size it draws at (#396), the Wayland clipboard with copied-file paste (#398), ruler guides that can be selected, moved and deleted (#414), two batches of Preferences options that were stored but unused (#394), skeleton reads with `document.find` for large documents over MCP (#416) and anchors dragged with Direct Selection snapping to points, segments and smart guides, clipped SVG layers opening as clipping layers (#420), PDF/`.ai` layers read on pages of tens of millions of operators (#419), Document Color Mode putting RGB greys on the black plate on request and converting pattern tiles (#421) and movable dialogs with Offset Path's Joins dropdown (#423) and numeric fields scrubbed by dragging their label (#400), stroke weight presets in the stroke unit (#426), the Appearance panel's Opacity popup (#430), type on a path brackets, flip and Type on a Path Options that keep the type in place (#429), Layers rows dragged with a ghost and dropped on the trash or New Layer (#435), tool flyouts opening on a right or corner press (#436), tool flyouts torn off into floating toolbars (#437), the Shape Builder's erase mode deleting edges too (#439), installed fonts registered outside the font folders and fonts installed while the app runs listed in the font menus (#443), alike circular corners on rectangles that aren't square (#442), tool cursors as OS cursors so the pointer no longer trails over the canvas (#444), artboards copied and pasted with their art (#446), guides dragged out of the rulers snapping to the art's side midpoints and the artboards, artboard guides (#451), the toolbar's double arrow and Window › Toolbars › Double Column switching between one and two columns in any window height (#455), the Smart Guides display options and Snapping Tolerance (#394), Font Size and Leading combo boxes with preset lists in the Character and Properties panels and the Control bar (#458), screenshots pasted from the clipboard on macOS, Windows and the web (#457), the Properties panel's Fill and Stroke matching the Control bar's, Illustrator's Window menu panel keys (Shift+F7 for Align…) and Alt-wheel zoom whatever the wheel preference (Discord feedback), the point/area type widget (Discord feedback), groups in legacy Illustrator .ai files (Discord feedback), a rotating desktop log file (#465), image controls in the Control bar and Properties panel: Image Trace with its presets, Mask, Crop Image, Embed/Unembed, and an Image Trace object's preset, Expand and Release (Discord feedback), the Pen working as the Anchor Point tool with Alt held over a selected path's handles and anchors, and anchor buttons that convert, connect and cut paths at the selected anchors (Discord feedback), Japanese hanging punctuation (burasagari, #463), Escape leaving Presentation Mode with no screen mode saved across restarts, documents dropped on the desktop app opening as tabs of their own and a note that an `.ai` file's art off its artboards isn't in the part read (#472), Add/Delete Anchor Point tool icons drawn from the Pen tool's (#479), EPS import running more of PostScript Level 3 (patterns, Type 3 fonts, mesh shadings, masked images, VM and resource probes), and a preview that stands in saying why (Discord feedback, #466, #469, #474), chords with modifiers recorded in Edit › Keyboard Shortcuts (#487), Type Object Selection by Path Only (#394), Anti-aliased Artwork (#394), Show Pixel Grid (#394), pen and touch taps pressed and lifted within one frame clicking where they tapped and the Linux/Wayland pen-tablet workaround (#491), bounding-box resizes landing on the other objects' anchors and centres with labels, and the Rotate, Scale, Shear and Reflect tools snapping their drags (#482), Shift-drag marquees that toggle the objects or anchors they reach and a Lasso whose Alt subtraction keeps the rest of the selection (#483), the mouse wheel stepping the focused numeric field, the dialogs' and preferences' drag values made such fields (#485), Show The Home Screen When No Documents Are Open (#394), Numbers Without Units Are Points (#394), Auto-Collapse Iconic Panels (#394), the Contextual Task Bar moved by its handle and its More Options menu pinning and resetting its position, a designed macOS DMG windoLine truncated
|
||||
_Last updated: 2026-10-09 (after placed documents (File › Place of a `.vectorcraft` file, linked), M4.14–M4.98, M8.1–M8.20, M14.4–M14.7, the MCP prompts, resource templates, completions and logging, the 2026-10-07 issue fixes, the Layers panel rework and the 2026-10-08 community contributions: Pen and shape-tool modifiers, path editing, Layers, Artboards, Swatches/Brushes and dialogs, Japanese composition and PDF text, Hebrew/Arabic type, interface languages (Spanish among them), the font menu, saved selections, MCP and EPS, the missing-font export warning (#319), open paths in the Shape Builder (#374), PDF/`.ai` import keeping strokes live and hidden and nested layers (#372), per-corner Live Corners (#381), SVG type opening at the size it draws at (#396), the Wayland clipboard with copied-file paste (#398), ruler guides that can be selected, moved and deleted (#414), two batches of Preferences options that were stored but unused (#394), skeleton reads with `document.find` for large documents over MCP (#416) and anchors dragged with Direct Selection snapping to points, segments and smart guides, clipped SVG layers opening as clipping layers (#420), PDF/`.ai` layers read on pages of tens of millions of operators (#419), Document Color Mode putting RGB greys on the black plate on request and converting pattern tiles (#421) and movable dialogs with Offset Path's Joins dropdown (#423) and numeric fields scrubbed by dragging their label (#400), stroke weight presets in the stroke unit (#426), the Appearance panel's Opacity popup (#430), type on a path brackets, flip and Type on a Path Options that keep the type in place (#429), Layers rows dragged with a ghost and dropped on the trash or New Layer (#435), tool flyouts opening on a right or corner press (#436), tool flyouts torn off into floating toolbars (#437), the Shape Builder's erase mode deleting edges too (#439), installed fonts registered outside the font folders and fonts installed while the app runs listed in the font menus (#443), alike circular corners on rectangles that aren't square (#442), tool cursors as OS cursors so the pointer no longer trails over the canvas (#444), artboards copied and pasted with their art (#446), guides dragged out of the rulers snapping to the art's side midpoints and the artboards, artboard guides (#451), the toolbar's double arrow and Window › Toolbars › Double Column switching between one and two columns in any window height (#455), the Smart Guides display options and Snapping Tolerance (#394), Font Size and Leading combo boxes with preset lists in the Character and Properties panels and the Control bar (#458), screenshots pasted from the clipboard on macOS, Windows and the web (#457), the Properties panel's Fill and Stroke matching the Control bar's, Illustrator's Window menu panel keys (Shift+F7 for Align…) and Alt-wheel zoom whatever the wheel preference (Discord feedback), the point/area type widget (Discord feedback), groups in legacy Illustrator .ai files (Discord feedback), a rotating desktop log file (#465), image controls in the Control bar and Properties panel: Image Trace with its presets, Mask, Crop Image, Embed/Unembed, and an Image Trace object's preset, Expand and Release (Discord feedback), the Pen working as the Anchor Point tool with Alt held over a selected path's handles and anchors, and anchor buttons that convert, connect and cut paths at the selected anchors (Discord feedback), Japanese hanging punctuation (burasagari, #463), Escape leaving Presentation Mode with no screen mode saved across restarts, documents dropped on the desktop app opening as tabs of their own and a note that an `.ai` file's art off its artboards isn't in the part read (#472), Add/Delete Anchor Point tool icons drawn from the Pen tool's (#479), EPS import running more of PostScript Level 3 (patterns, Type 3 fonts, mesh shadings, masked images, VM and resource probes), and a preview that stands in saying why (Discord feedback, #466, #469, #474), chords with modifiers recorded in Edit › Keyboard Shortcuts (#487), Type Object Selection by Path Only (#394), Anti-aliased Artwork (#394), Show Pixel Grid (#394), pen and touch taps pressed and lifted within one frame clicking where they tapped and the Linux/Wayland pen-tablet workaround (#491), bounding-box resizes landing on the other objects' anchors and centres with labels, and the Rotate, Scale, Shear and Reflect tools snapping their drags (#482), Shift-drag marquees that toggle the objects or anchors they reach and a Lasso whose Alt subtraction keeps the rest of the selection (#483), the mouse wheel stepping the focused numeric field, the dialogs' and preferences' drag values made such fields (#485), Show The Home Screen When No Documents Are Open (#394), Numbers Without Units Are Points (#394), Auto-Collapse Iconic Panels (#394), the Contextual Task Bar moved by its handle and its More Options menu pinning and resetting its position, a designed macOS DMG windoLine truncated
|
||||
|
||||
## Where we are
|
||||
|
||||
|
||||
@@ -22,8 +22,9 @@ use std::sync::{Arc, OnceLock};
|
||||
use eframe::egui_wgpu::NativeAdapterSelectorMethod;
|
||||
use eframe::wgpu::{self, Backend, DeviceType, PowerPreference};
|
||||
|
||||
/// The adapters a restart leaves out, those the window failed on, as comma-separated
|
||||
/// `backend:vendor:device` keys (`Vulkan:1002:164e`). Set by the app itself when it starts again.
|
||||
/// The adapters a restart leaves out, those the window failed on, as comma-separated [`key`]s
|
||||
/// (`Vulkan:1002:164e`, `Metal:Intel Iris Pro Graphics`). Set by the app itself when it starts
|
||||
/// again.
|
||||
pub const SKIP_ENV: &str = "VECTORCRAFT_GPU_SKIP";
|
||||
|
||||
/// wgpu's variable for choosing an adapter by (part of) its name, any case.
|
||||
@@ -60,7 +61,7 @@ pub fn power_preference(pref: Option<&str>, env: Option<PowerPreference>) -> Pow
|
||||
/// One adapter, as [`adapter_order`] sees it.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Candidate {
|
||||
/// Identifies the adapter across a restart: `backend:vendor:device` ([`key`]).
|
||||
/// Identifies the adapter across a restart ([`key`]).
|
||||
pub key: String,
|
||||
pub name: String,
|
||||
pub backend: Backend,
|
||||
@@ -73,7 +74,7 @@ impl Candidate {
|
||||
fn new(adapter: &wgpu::Adapter, surface: Option<&wgpu::Surface<'_>>) -> Self {
|
||||
let info = adapter.get_info();
|
||||
Self {
|
||||
key: key(info.backend, info.vendor, info.device),
|
||||
key: key(info.backend, info.vendor, info.device, &info.name),
|
||||
presents: surface.is_none_or(|s| adapter.is_surface_supported(s)),
|
||||
name: info.name,
|
||||
backend: info.backend,
|
||||
@@ -83,8 +84,15 @@ impl Candidate {
|
||||
}
|
||||
|
||||
/// `backend:vendor:device`, e.g. `Vulkan:1002:164e` (PCI ids, as `MESA_VK_DEVICE_SELECT` takes them).
|
||||
fn key(backend: Backend, vendor: u32, device: u32) -> String {
|
||||
format!("{backend:?}:{vendor:04x}:{device:04x}")
|
||||
/// Metal reports no ids (`0000:0000` for every GPU), so an adapter without them is `backend:name`
|
||||
/// (`Metal:Intel Iris Pro Graphics`): otherwise leaving out the one that failed would leave out
|
||||
/// every GPU of a dual-GPU Mac (#651). Commas, which separate [`SKIP_ENV`]'s keys, become spaces.
|
||||
fn key(backend: Backend, vendor: u32, device: u32, name: &str) -> String {
|
||||
if vendor == 0 && device == 0 {
|
||||
format!("{backend:?}:{}", name.replace(',', " ").trim())
|
||||
} else {
|
||||
format!("{backend:?}:{vendor:04x}:{device:04x}")
|
||||
}
|
||||
}
|
||||
|
||||
/// The order to try `candidates` in (their indices): only those that present to the window and
|
||||
@@ -307,6 +315,27 @@ mod tests {
|
||||
|
||||
/// A restart leaves out the adapter the window failed on, so Power Saving on the machine of
|
||||
/// #502 ends up on the NVIDIA GPU, and once every adapter failed there is nothing to try.
|
||||
/// A dual-GPU Mac reports both GPUs under Metal with no PCI ids (#651): their keys still
|
||||
/// differ, so when the integrated GPU fails, the restart renders on the discrete one.
|
||||
#[test]
|
||||
fn gpus_without_pci_ids_are_told_apart_by_name() {
|
||||
let mac = |name: &str, device_type| Candidate {
|
||||
key: key(Backend::Metal, 0, 0, name),
|
||||
name: name.into(),
|
||||
backend: Backend::Metal,
|
||||
device_type,
|
||||
presents: true,
|
||||
};
|
||||
let c = vec![mac("NVIDIA GeForce GT 750M", DeviceType::DiscreteGpu), mac("Intel Iris Pro Graphics", DeviceType::IntegratedGpu)];
|
||||
assert_eq!((c[0].key.as_str(), c[1].key.as_str()), ("Metal:NVIDIA GeForce GT 750M", "Metal:Intel Iris Pro Graphics"));
|
||||
let first = adapter_order(&c, PowerPreference::LowPower, None, &[]);
|
||||
assert_eq!(names(&c, &first)[0], "Intel Iris Pro Graphics");
|
||||
let skip = parse_skip(&c[first[0]].key);
|
||||
assert_eq!(names(&c, &adapter_order(&c, PowerPreference::LowPower, None, &skip)), ["NVIDIA GeForce GT 750M"]);
|
||||
// A comma in a name can't split the list.
|
||||
assert_eq!(parse_skip(&key(Backend::Gl, 0, 0, "Mesa, llvmpipe")), ["Gl:Mesa llvmpipe"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_restart_tries_the_next_adapter() {
|
||||
let c = issue_502();
|
||||
@@ -433,8 +462,8 @@ mod tests {
|
||||
fn skipped_adapters_parse_from_a_comma_separated_list() {
|
||||
assert_eq!(parse_skip("Vulkan:1002:164e, Gl:10de:2f04,,"), ["Vulkan:1002:164e", "Gl:10de:2f04"]);
|
||||
assert!(parse_skip("").is_empty());
|
||||
assert_eq!(key(Backend::Vulkan, 0x1002, 0x164e), "Vulkan:1002:164e");
|
||||
assert_eq!(key(Backend::Gl, 0x10de, 0x2f04), "Gl:10de:2f04");
|
||||
assert_eq!(key(Backend::Vulkan, 0x1002, 0x164e, "AMD Radeon"), "Vulkan:1002:164e");
|
||||
assert_eq!(key(Backend::Gl, 0x10de, 0x2f04, "NVIDIA"), "Gl:10de:2f04");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -423,6 +423,11 @@ fn main() -> std::process::ExitCode {
|
||||
// Only adapters that can show the window, in the order `gpu` gives (#306, #502).
|
||||
if let eframe::egui_wgpu::WgpuSetup::CreateNew(create) = &mut options.wgpu_options.wgpu_setup {
|
||||
create.native_adapter_selector = Some(gpu::selector(power, startup.clone()));
|
||||
// Nothing draws or dispatches indirectly, so wgpu's check of indirect arguments only
|
||||
// costs a compute shader at start-up, one some drivers can't compile (OCLP-patched
|
||||
// Metal on an Iris Pro, #651). `WGPU_VALIDATION_INDIRECT_CALL=1` turns it back on.
|
||||
create.instance_descriptor.flags =
|
||||
(eframe::wgpu::InstanceFlags::from_build_config() - eframe::wgpu::InstanceFlags::VALIDATION_INDIRECT_CALL).with_env();
|
||||
}
|
||||
options
|
||||
};
|
||||
|
||||
+2
-1
@@ -43,11 +43,12 @@ The canvas is rasterized on the CPU (`vectorcraft-render`, vello_cpu); the GPU (
|
||||
- **Automatic on Windows and macOS** is power saving: the system shows frames from any GPU, and presenting frames rendered on a discrete GPU through the integrated one made the window flicker on some hybrid laptops (#306).
|
||||
- **Automatic on Linux and the BSDs** keeps the system's order: Mesa's Vulkan device-select layer puts the GPU the desktop runs on first (the integrated one on hybrid laptops; `DRI_PRIME` and `MESA_VK_DEVICE_SELECT` steer it). A Wayland compositor may not accept frames from another GPU: on a desktop whose compositor ran on an NVIDIA GPU, rendering on the Ryzen's integrated GPU made KWin end the window's connection ("importing the supplied dmabufs failed") and 0.5.0 crashed at startup (#502).
|
||||
- 0.5.0 saved `powerSaving`, its default, for everyone; it reads as `automatic`.
|
||||
- **If the window fails on its adapter while starting up** (an error from wgpu, or a panic inside wgpu or egui-wgpu during the first frames), the app starts again without that adapter and tries the next one, telling which in the status bar; the run that failed keeps its log as `vectorcraft.1.log`. On Unix the new app replaces the process (an AppImage stays mounted); on Windows it starts beside it. The adapters left out travel in `VECTORCRAFT_GPU_SKIP` (`backend:vendor:device`, such as `Vulkan:1002:164e`), one more on each restart, so the restarts end. When no adapter is left, the app logs why and exits with an error instead of panicking.
|
||||
- **If the window fails on its adapter while starting up** (an error from wgpu, or a panic inside wgpu or egui-wgpu during the first frames), the app starts again without that adapter and tries the next one, telling which in the status bar; the run that failed keeps its log as `vectorcraft.1.log`. On Unix the new app replaces the process (an AppImage stays mounted); on Windows it starts beside it. Adapters are told apart by backend and PCI ids, or by name where the backend reports no ids (Metal lists every GPU of a dual-GPU Mac as `0000:0000`, #651). The adapters left out travel in `VECTORCRAFT_GPU_SKIP` (`backend:vendor:device`, such as `Vulkan:1002:164e`), one more on each restart, so the restarts end. When no adapter is left, the app logs why and exits with an error instead of panicking.
|
||||
- **Environment variables** for when the choice still goes wrong, all read at startup and stronger than the preference:
|
||||
- `WGPU_POWER_PREF`: `low`, `high` or `none` (the system's order).
|
||||
- `WGPU_ADAPTER_NAME`: part of an adapter's name, any case (`WGPU_ADAPTER_NAME=nvidia`, `=radv`, `=intel`); the names are in the log.
|
||||
- `WGPU_BACKEND`: the backends to use, such as `vulkan`, `dx12`, `metal` or `gl`.
|
||||
- `WGPU_VALIDATION_INDIRECT_CALL=1`: turns wgpu's check of indirect draw arguments back on. VectorCraft draws nothing indirectly and turns it off, since the compute shader it needs fails to compile on some drivers (#651).
|
||||
- On Linux with Mesa, `MESA_VK_DEVICE_SELECT=10de:2f04!` (vendor:device as `vulkaninfo --summary` or `lspci -nn` show it) leaves only that GPU to Vulkan, and `DRI_PRIME=1` picks the other GPU.
|
||||
- Help › About and the control channel's `ui.inspect` (`graphicsAdapter`) show the adapter in use, and the app logs it at startup.
|
||||
|
||||
|
||||
Reference in new issue
Block a user