mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-30 13:26:54 -06:00
Direct3D 12 compiles with FXC and no device compiles wgpu's indirect-call validation shaders, the two causes of "Parent device is lost" at start. When the graphics still fail before the first frame, the app starts again with the next of a fixed list of attempts (Windows: Vulkan, OpenGL; Linux and FreeBSD: OpenGL; then lower requirements on the power-saving GPU), and the last failure's message names what was tried.
This commit is contained in:
+2
-1
File diff suppressed because one or more lines are too long
@@ -59,8 +59,20 @@ pub fn drain_queue(queue: &Queue, target: &mut Vec<ControlRequest>) {
|
||||
target.extend(queue.drain(..count));
|
||||
}
|
||||
|
||||
pub fn start(port: u16, queue: Queue, ctx: CtxSlot, events: Arc<EventLog>) {
|
||||
let listener = match TcpListener::bind(("127.0.0.1", port)) {
|
||||
/// Bind `port`, waiting up to `busy` for it while another process holds it: an app started again on
|
||||
/// other graphics ([`crate::graphics`]) starts beside the one ending, which still holds it.
|
||||
pub fn bind(port: u16, busy: Duration) -> std::io::Result<TcpListener> {
|
||||
let since = std::time::Instant::now();
|
||||
loop {
|
||||
match TcpListener::bind(("127.0.0.1", port)) {
|
||||
Err(e) if e.kind() == std::io::ErrorKind::AddrInUse && since.elapsed() < busy => std::thread::sleep(Duration::from_millis(50)),
|
||||
bound => return bound,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(port: u16, busy: Duration, queue: Queue, ctx: CtxSlot, events: Arc<EventLog>) {
|
||||
let listener = match bind(port, busy) {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
log::error!("control server: can't bind 127.0.0.1:{port}: {e} (another app may own it; pick another port)");
|
||||
|
||||
+164
-15
@@ -1,4 +1,4 @@
|
||||
//! Which graphics API draws the window.
|
||||
//! Which graphics API draws the window, and what happens when it can't.
|
||||
//!
|
||||
//! eframe offers wgpu every backend it was built with, and between equal GPUs wgpu takes Vulkan
|
||||
//! first, on Windows too. A Vulkan driver that faults there takes the app down as the window opens:
|
||||
@@ -6,29 +6,178 @@
|
||||
//! with two AMD GPUs whose drivers were years apart). Direct3D 12 is part of Windows, so on Windows
|
||||
//! wgpu is offered Direct3D 12, and OpenGL for hardware without a Direct3D 12 driver.
|
||||
//!
|
||||
//! Two shader compilers lost the device before the window opened ("Parent device is lost", #965,
|
||||
//! #1438), both on wgpu's indirect-call validation shaders, which wgpu compiles with every device:
|
||||
//! Direct3D 12 took the first `dxcompiler.dll` on the search path, another program's, and failed
|
||||
//! on the missing `dxil.dll` beside it; Metal's Intel compiler aborts on them on older Macs. So
|
||||
//! Direct3D 12 compiles with FXC, which is part of Windows, and the validation shaders are left
|
||||
//! out: FigCraft draws nothing indirectly, so they would check nothing.
|
||||
//!
|
||||
//! When the graphics still fail before the window's first frame, the app starts again with the
|
||||
//! next of its [`attempts`]: on Windows Vulkan, then OpenGL; elsewhere OpenGL; and last the first
|
||||
//! backends again with lower requirements on the power-saving GPU (macOS has no OpenGL in wgpu, so
|
||||
//! that is its only other try). The attempts are a fixed list, so this ends; the message the last
|
||||
//! one shows names what was tried.
|
||||
//!
|
||||
//! `WGPU_BACKEND` (`dx12`, `vulkan`, `gl`, `metal`) still names the backend on every system, and
|
||||
//! systems other than Windows keep eframe's defaults.
|
||||
//! is then the one attempt. `WGPU_DX12_COMPILER` and `WGPU_VALIDATION_INDIRECT_CALL` still choose
|
||||
//! the compiler and the validation.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use eframe::egui_wgpu::{WgpuConfiguration, WgpuSetup};
|
||||
use eframe::wgpu::Backends;
|
||||
use eframe::wgpu::{self, Backends, Dx12Compiler, InstanceFlags, Limits, PowerPreference};
|
||||
|
||||
/// Which of the [`attempts`] this start is: set by the app itself when it starts again.
|
||||
pub const ATTEMPT_ENV: &str = "FIGCRAFT_GRAPHICS_ATTEMPT";
|
||||
|
||||
/// Frames drawn before a failure below which the window was still starting up: the second frame
|
||||
/// begins once the first was presented.
|
||||
pub const STARTUP_FRAMES: u64 = 2;
|
||||
|
||||
/// Direct3D 12, then OpenGL: wgpu ranks a hardware GPU on either ahead of Direct3D's software one.
|
||||
const WINDOWS: Backends = Backends::DX12.union(Backends::GL);
|
||||
|
||||
/// The backends wgpu chooses from: the ones `WGPU_BACKEND` asks for (`requested`), else [`WINDOWS`]
|
||||
/// on Windows, else eframe's `defaults`.
|
||||
pub fn backends(requested: Option<Backends>, windows: bool, defaults: Backends) -> Backends {
|
||||
match requested {
|
||||
Some(requested) => requested,
|
||||
None if windows => WINDOWS,
|
||||
None => defaults,
|
||||
/// The graphics APIs by name, in the order the startup message lists them.
|
||||
const NAMES: [(Backends, &str); 4] =
|
||||
[(Backends::DX12, "Direct3D 12"), (Backends::METAL, "Metal"), (Backends::VULKAN, "Vulkan"), (Backends::GL, "OpenGL")];
|
||||
|
||||
/// One way of starting the graphics.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Attempt {
|
||||
/// The backends wgpu chooses from.
|
||||
pub backends: Backends,
|
||||
/// The power-saving GPU and only the device limits OpenGL ES 3 hardware has.
|
||||
pub lower: bool,
|
||||
}
|
||||
|
||||
/// The ways to start, in order: only `WGPU_BACKEND`'s backends (`requested`) when it is set;
|
||||
/// otherwise [`WINDOWS`] on Windows, else eframe's `defaults`, then Vulkan on Windows, OpenGL
|
||||
/// except on macOS, and those first backends again with lower requirements.
|
||||
pub fn attempts(requested: Option<Backends>, windows: bool, macos: bool, defaults: Backends) -> Vec<Attempt> {
|
||||
let normal = |backends| Attempt { backends, lower: false };
|
||||
if let Some(requested) = requested {
|
||||
return vec![normal(requested)];
|
||||
}
|
||||
let first = if windows { WINDOWS } else { defaults };
|
||||
let mut list = vec![normal(first)];
|
||||
if windows {
|
||||
list.push(normal(Backends::VULKAN));
|
||||
}
|
||||
if !macos && first != Backends::GL {
|
||||
list.push(normal(Backends::GL));
|
||||
}
|
||||
list.push(Attempt { backends: first, lower: true });
|
||||
list
|
||||
}
|
||||
|
||||
/// Which of `count` attempts this start is, from [`ATTEMPT_ENV`] (`value`): the first when it is
|
||||
/// unset or not a number, the last when it is past the end.
|
||||
pub fn current(value: Option<&str>, count: usize) -> usize {
|
||||
value.and_then(|v| v.trim().parse().ok()).unwrap_or(0).min(count.saturating_sub(1))
|
||||
}
|
||||
|
||||
/// The attempt to start again with after `attempt` of `count` ended: the next one, when the
|
||||
/// graphics failed (`graphics_failed`) before the window's first frame and one is left.
|
||||
pub fn next(attempt: usize, count: usize, graphics_failed: bool, frames: u64) -> Option<usize> {
|
||||
let next = attempt + 1;
|
||||
(graphics_failed && frames < STARTUP_FRAMES && next < count).then_some(next)
|
||||
}
|
||||
|
||||
/// How long this start waits for its control port: one started again ([`ATTEMPT_ENV`]) starts
|
||||
/// beside the app that is ending, which still holds it for a moment.
|
||||
pub fn port_wait() -> Duration {
|
||||
if std::env::var_os(ATTEMPT_ENV).is_some() { Duration::from_secs(5) } else { Duration::ZERO }
|
||||
}
|
||||
|
||||
/// The graphics APIs `attempts` tried among those this build has (`built`), by name.
|
||||
pub fn names(attempts: &[Attempt], built: Backends) -> String {
|
||||
let tried = attempts.iter().fold(Backends::empty(), |all, a| all | a.backends) & built;
|
||||
NAMES.iter().filter(|(b, _)| tried.contains(*b)).map(|(_, name)| *name).collect::<Vec<_>>().join(", ")
|
||||
}
|
||||
|
||||
/// Set up `options` for `attempt` (see the module docs).
|
||||
pub fn apply(options: &mut WgpuConfiguration, attempt: Attempt) {
|
||||
let WgpuSetup::CreateNew(create) = &mut options.wgpu_setup else { return };
|
||||
let instance = &mut create.instance_descriptor;
|
||||
instance.backends = attempt.backends;
|
||||
instance.backend_options.dx12.shader_compiler = Dx12Compiler::from_env().unwrap_or(Dx12Compiler::Fxc);
|
||||
instance.flags = instance.flags.difference(InstanceFlags::VALIDATION_INDIRECT_CALL).with_env();
|
||||
if attempt.lower {
|
||||
create.power_preference = PowerPreference::from_env().unwrap_or(PowerPreference::LowPower);
|
||||
create.device_descriptor = Arc::new(|adapter| wgpu::DeviceDescriptor {
|
||||
label: Some("egui wgpu device"),
|
||||
// The window's surface still needs textures as large as the adapter allows.
|
||||
required_limits: Limits::downlevel_webgl2_defaults().using_resolution(adapter.limits()),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Offer wgpu [`backends`] instead of eframe's defaults.
|
||||
pub fn configure(options: &mut WgpuConfiguration) {
|
||||
if let WgpuSetup::CreateNew(create) = &mut options.wgpu_setup {
|
||||
let offered = &mut create.instance_descriptor.backends;
|
||||
*offered = backends(Backends::from_env(), cfg!(windows), *offered);
|
||||
/// The attempts of this run and which one it is.
|
||||
pub struct Plan {
|
||||
attempts: Vec<Attempt>,
|
||||
current: usize,
|
||||
}
|
||||
|
||||
impl Plan {
|
||||
/// Set up `options` for this start's attempt.
|
||||
pub fn configure(options: &mut WgpuConfiguration) -> Self {
|
||||
let defaults = match &options.wgpu_setup {
|
||||
WgpuSetup::CreateNew(create) => create.instance_descriptor.backends,
|
||||
WgpuSetup::Existing(_) => Backends::all(),
|
||||
};
|
||||
let attempts = attempts(Backends::from_env(), cfg!(windows), cfg!(target_os = "macos"), defaults);
|
||||
let current = current(std::env::var(ATTEMPT_ENV).ok().as_deref(), attempts.len());
|
||||
if let Some(attempt) = attempts.get(current) {
|
||||
apply(options, *attempt);
|
||||
}
|
||||
Self { attempts, current }
|
||||
}
|
||||
|
||||
/// The window opened on `adapter`: which GPU draws is the first question in every report.
|
||||
pub fn opened(&self, adapter: &wgpu::AdapterInfo) {
|
||||
let text =
|
||||
format!("graphics: drawing with {:?} on {} ({:?}, driver {})", adapter.backend, adapter.name.trim(), adapter.device_type, adapter.driver);
|
||||
if self.current == 0 {
|
||||
log::info!("{text}");
|
||||
} else {
|
||||
log::warn!("{text}, start {} of {}: {:?}", self.current + 1, self.attempts.len(), self.attempts.get(self.current));
|
||||
}
|
||||
}
|
||||
|
||||
/// After the window's run (`result`, after `frames` frames): start again with the next attempt
|
||||
/// when the graphics failed while the window was starting up, otherwise say why it failed
|
||||
/// ([`crate::startup::failed`], `quiet` as there).
|
||||
pub fn finish(&self, result: eframe::Result, frames: u64, quiet: bool) -> eframe::Result {
|
||||
let Err(error) = &result else { return result };
|
||||
if let Some(next) = next(self.current, self.attempts.len(), matches!(error, eframe::Error::Wgpu(_)), frames) {
|
||||
log::warn!("graphics: {error}; starting again with {:?}", self.attempts.get(next));
|
||||
match restart(next) {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(e) => log::error!("graphics: starting again failed: {e}"),
|
||||
}
|
||||
}
|
||||
let last = self.current > 0 && self.current + 1 == self.attempts.len();
|
||||
let tried = last.then(|| names(&self.attempts, wgpu::Instance::enabled_backend_features()));
|
||||
crate::startup::failed(error, quiet, tried.as_deref());
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Start the app again with the same arguments as attempt `attempt`. On Unix the new app replaces
|
||||
/// this process (an AppImage keeps its files mounted), and this returns only on failure; elsewhere
|
||||
/// it starts beside this one, which then exits.
|
||||
fn restart(attempt: usize) -> std::io::Result<()> {
|
||||
log::logger().flush();
|
||||
let mut command = std::process::Command::new(std::env::current_exe()?);
|
||||
command.args(std::env::args_os().skip(1)).env(ATTEMPT_ENV, attempt.to_string());
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::process::CommandExt as _;
|
||||
Err(command.exec())
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
command.spawn().map(|_| ())
|
||||
}
|
||||
|
||||
@@ -1,32 +1,148 @@
|
||||
//! The backends offered to wgpu: no Vulkan on Windows unless it is asked for (a faulting Vulkan
|
||||
//! driver crashed the app as its window opened), and nothing changed anywhere else.
|
||||
//! The graphics attempts: no Vulkan on Windows at first (a faulting Vulkan driver crashed the app
|
||||
//! as its window opened), another backend and then lower requirements when the graphics fail
|
||||
//! before the first frame (#965, #1438), and never more starts than attempts.
|
||||
|
||||
use eframe::wgpu::Backends;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::graphics::backends;
|
||||
use eframe::egui_wgpu::{WgpuConfiguration, WgpuError, WgpuSetup, WgpuSetupCreateNew};
|
||||
use eframe::wgpu::{Backends, Dx12Compiler, InstanceFlags, PowerPreference};
|
||||
|
||||
use crate::control_server::bind;
|
||||
use crate::graphics::{ATTEMPT_ENV, Attempt, Plan, STARTUP_FRAMES, apply, attempts, current, names, next, port_wait};
|
||||
|
||||
/// What eframe offers when `WGPU_BACKEND` isn't set.
|
||||
const DEFAULTS: Backends = Backends::PRIMARY.union(Backends::GL);
|
||||
|
||||
#[test]
|
||||
fn windows_is_offered_direct3d_and_not_vulkan() {
|
||||
let offered = backends(None, true, DEFAULTS);
|
||||
assert!(offered.contains(Backends::DX12), "Direct3D 12 draws the window");
|
||||
assert!(offered.contains(Backends::GL), "OpenGL is there for hardware without a Direct3D 12 driver");
|
||||
assert!(!offered.contains(Backends::VULKAN), "a Vulkan driver that faults can't be picked");
|
||||
const DX12_GL: Backends = Backends::DX12.union(Backends::GL);
|
||||
|
||||
const fn normal(backends: Backends) -> Attempt {
|
||||
Attempt { backends, lower: false }
|
||||
}
|
||||
|
||||
const fn lower(backends: Backends) -> Attempt {
|
||||
Attempt { backends, lower: true }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wgpu_backend_names_the_backend_on_every_system() {
|
||||
for windows in [true, false] {
|
||||
fn windows_starts_on_direct3d_then_vulkan_then_opengl_then_lower_requirements() {
|
||||
let list = attempts(None, true, false, DEFAULTS);
|
||||
assert_eq!(list, [normal(DX12_GL), normal(Backends::VULKAN), normal(Backends::GL), lower(DX12_GL)]);
|
||||
assert!(!list[0].backends.contains(Backends::VULKAN), "a Vulkan driver that faults can't be picked at first");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linux_falls_back_to_opengl_then_lower_requirements() {
|
||||
assert_eq!(attempts(None, false, false, DEFAULTS), [normal(DEFAULTS), normal(Backends::GL), lower(DEFAULTS)]);
|
||||
// OpenGL alone is tried once.
|
||||
assert_eq!(attempts(None, false, false, Backends::GL), [normal(Backends::GL), lower(Backends::GL)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macos_has_no_opengl_so_it_only_lowers_the_requirements() {
|
||||
assert_eq!(attempts(None, false, true, DEFAULTS), [normal(DEFAULTS), lower(DEFAULTS)]);
|
||||
assert_eq!(attempts(None, false, true, Backends::METAL), [normal(Backends::METAL), lower(Backends::METAL)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wgpu_backend_names_the_one_attempt_on_every_system() {
|
||||
for (windows, macos) in [(true, false), (false, false), (false, true)] {
|
||||
for asked in [Backends::VULKAN, Backends::DX12, Backends::GL, Backends::VULKAN | Backends::GL] {
|
||||
assert_eq!(backends(Some(asked), windows, DEFAULTS), asked, "windows: {windows}");
|
||||
assert_eq!(attempts(Some(asked), windows, macos, DEFAULTS), [normal(asked)], "windows: {windows}, macos: {macos}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn other_systems_keep_eframes_defaults() {
|
||||
assert_eq!(backends(None, false, DEFAULTS), DEFAULTS);
|
||||
assert_eq!(backends(None, false, Backends::METAL), Backends::METAL);
|
||||
fn the_attempt_comes_from_the_restart_and_stays_in_range() {
|
||||
assert_eq!(current(None, 4), 0);
|
||||
assert_eq!(current(Some("2"), 4), 2);
|
||||
assert_eq!(current(Some(" 1 "), 4), 1);
|
||||
assert_eq!(current(Some("9"), 4), 3, "past the end is the last");
|
||||
assert_eq!(current(Some("-1"), 4), 0);
|
||||
assert_eq!(current(Some("junk"), 4), 0);
|
||||
assert_eq!(current(Some("1"), 0), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_a_graphics_failure_while_starting_up_starts_again_and_never_past_the_last_attempt() {
|
||||
assert_eq!(next(0, 4, true, 0), Some(1));
|
||||
assert_eq!(next(2, 4, true, STARTUP_FRAMES - 1), Some(3));
|
||||
assert_eq!(next(3, 4, true, 0), None, "the last attempt ends it");
|
||||
assert_eq!(next(0, 1, true, 0), None, "WGPU_BACKEND's one attempt ends it");
|
||||
assert_eq!(next(0, 4, false, 0), None, "not the graphics");
|
||||
assert_eq!(next(0, 4, true, STARTUP_FRAMES), None, "the window ran");
|
||||
// Following the restarts from the first start ends within the list.
|
||||
for count in 1..6 {
|
||||
let starts = std::iter::successors(Some(0), |&a| next(a, count, true, 0)).count();
|
||||
assert_eq!(starts, count);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_message_names_the_apis_this_build_has() {
|
||||
let windows = attempts(None, true, false, DEFAULTS);
|
||||
assert_eq!(names(&windows, Backends::all()), "Direct3D 12, Vulkan, OpenGL");
|
||||
let linux = attempts(None, false, false, DEFAULTS);
|
||||
assert_eq!(names(&linux, Backends::VULKAN | Backends::GL), "Vulkan, OpenGL");
|
||||
let macos = attempts(None, false, true, DEFAULTS);
|
||||
assert_eq!(names(&macos, Backends::METAL), "Metal");
|
||||
}
|
||||
|
||||
fn create_new(options: &WgpuConfiguration) -> Option<&WgpuSetupCreateNew> {
|
||||
match &options.wgpu_setup {
|
||||
WgpuSetup::CreateNew(create) => Some(create),
|
||||
WgpuSetup::Existing(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_attempt_compiles_with_fxc_and_leaves_the_indirect_validation_out() {
|
||||
for attempt in [normal(Backends::DX12), lower(Backends::METAL)] {
|
||||
let mut options = WgpuConfiguration::default();
|
||||
let power = create_new(&options).map(|c| c.power_preference);
|
||||
apply(&mut options, attempt);
|
||||
let create = create_new(&options).expect("eframe creates the instance");
|
||||
assert_eq!(create.instance_descriptor.backends, attempt.backends);
|
||||
if std::env::var_os("WGPU_DX12_COMPILER").is_none() {
|
||||
assert!(matches!(create.instance_descriptor.backend_options.dx12.shader_compiler, Dx12Compiler::Fxc));
|
||||
}
|
||||
if std::env::var_os("WGPU_VALIDATION_INDIRECT_CALL").is_none() {
|
||||
assert!(!create.instance_descriptor.flags.contains(InstanceFlags::VALIDATION_INDIRECT_CALL));
|
||||
}
|
||||
if std::env::var_os("WGPU_POWER_PREF").is_none() {
|
||||
let want = if attempt.lower { Some(PowerPreference::LowPower) } else { power };
|
||||
assert_eq!(Some(create.power_preference), want);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_start_again_waits_for_the_control_port_the_ending_app_still_holds() {
|
||||
let held = std::net::TcpListener::bind(("127.0.0.1", 0)).expect("a free port");
|
||||
let port = held.local_addr().expect("its address").port();
|
||||
let busy = bind(port, Duration::ZERO).expect_err("the port is held");
|
||||
assert_eq!(busy.kind(), std::io::ErrorKind::AddrInUse);
|
||||
let release = std::thread::spawn(move || {
|
||||
std::thread::sleep(Duration::from_millis(200));
|
||||
drop(held);
|
||||
});
|
||||
assert!(bind(port, Duration::from_secs(10)).is_ok(), "bound once the other app let go");
|
||||
release.join().expect("released");
|
||||
if std::env::var_os(ATTEMPT_ENV).is_none() {
|
||||
assert_eq!(port_wait(), Duration::ZERO, "a first start doesn't wait");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_failure_that_starts_nothing_again_ends_with_the_error() {
|
||||
if std::env::var_os(ATTEMPT_ENV).is_some() {
|
||||
return;
|
||||
}
|
||||
let mut options = WgpuConfiguration::default();
|
||||
let plan = Plan::configure(&mut options);
|
||||
assert!(plan.finish(Ok(()), 0, true).is_ok());
|
||||
// A failure that isn't the graphics', and a graphics failure once the window ran: no restart.
|
||||
assert!(plan.finish(Err(eframe::Error::AppCreation("no app".into())), 0, true).is_err());
|
||||
let graphics = eframe::Error::Wgpu(WgpuError::NoSurfaceFormatsAvailable);
|
||||
assert!(plan.finish(Err(graphics), STARTUP_FRAMES, true).is_err());
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ fn main() -> eframe::Result {
|
||||
}
|
||||
let queue: Arc<Mutex<Vec<ControlRequest>>> = Arc::new(Mutex::new(Vec::new()));
|
||||
if let Some(p) = port {
|
||||
control_server::start(p, queue.clone(), ctx_slot.clone(), fig.session.events.clone());
|
||||
control_server::start(p, graphics::port_wait(), queue.clone(), ctx_slot.clone(), fig.session.events.clone());
|
||||
}
|
||||
let icon = services::window_icon();
|
||||
// The app id is the Wayland app_id and the X11 window class on Linux: the desktop entry's name
|
||||
@@ -258,7 +258,9 @@ fn main() -> eframe::Result {
|
||||
viewport = viewport.with_icon(icon);
|
||||
}
|
||||
let mut options = eframe::NativeOptions { viewport, renderer: eframe::Renderer::Wgpu, ..Default::default() };
|
||||
graphics::configure(&mut options.wgpu_options);
|
||||
let graphics = graphics::Plan::configure(&mut options.wgpu_options);
|
||||
// Counts the frames drawn, which tells a window that failed starting up from one that ran.
|
||||
let window = ctx_slot.clone();
|
||||
figcraft_ui_egui::windows::install(fig);
|
||||
// Registered before the event loop starts, so it catches the Finder event that launched the app
|
||||
// as well as later ones. Lives until the event loop returns; the app creator only borrows it.
|
||||
@@ -269,7 +271,10 @@ fn main() -> eframe::Result {
|
||||
let result = eframe::run_native(
|
||||
"FigCraft",
|
||||
options,
|
||||
Box::new(move |cc| {
|
||||
Box::new(|cc| {
|
||||
if let Some(state) = &cc.wgpu_render_state {
|
||||
graphics.opened(&state.adapter.get_info());
|
||||
}
|
||||
with_app(|fig| fig.services.set_egui_context(&cc.egui_ctx));
|
||||
// winit's own Quit (⌘Q) ends the process through `terminate:` without ever asking the
|
||||
// app, so unsaved changes are lost with no warning (issue #857). This menu's Quit
|
||||
@@ -290,11 +295,10 @@ fn main() -> eframe::Result {
|
||||
}))
|
||||
}),
|
||||
);
|
||||
// Say why instead of quitting silently: release builds on Windows have no console.
|
||||
if let Err(e) = &result {
|
||||
startup::failed(e, port.is_some());
|
||||
}
|
||||
result
|
||||
let frames = window.lock().unwrap_or_else(std::sync::PoisonError::into_inner).as_ref().map_or(0, egui::Context::cumulative_frame_nr);
|
||||
// Start again on other graphics, or say why instead of quitting silently: release builds on
|
||||
// Windows have no console.
|
||||
graphics.finish(result, frames, port.is_some())
|
||||
}
|
||||
|
||||
/// With nothing to open, whether the window starts in a new file rather than on the home screen.
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
//! and the app quit without a word (#261). Now it is logged and shown in a message box.
|
||||
|
||||
/// Log the failure and, unless `quiet` (started for an agent with `--control`: a modal box would
|
||||
/// keep the caller waiting, and it reads standard error), show it in a message box.
|
||||
pub fn failed(error: &eframe::Error, quiet: bool) {
|
||||
/// keep the caller waiting, and it reads standard error), show it in a message box. `tried`: the
|
||||
/// graphics APIs the app started again with before this failure ([`crate::graphics::Plan::finish`]).
|
||||
pub fn failed(error: &eframe::Error, quiet: bool, tried: Option<&str>) {
|
||||
// In the interface language: the text and its catalog entries are the UI crate's.
|
||||
let text = figcraft_ui_egui::dialogs::window_failed(&error.to_string(), std::env::var_os("WGPU_BACKEND").is_some());
|
||||
let text = figcraft_ui_egui::dialogs::window_failed(&error.to_string(), std::env::var_os("WGPU_BACKEND").is_some(), tried);
|
||||
// Standard error even when RUST_LOG turned errors off.
|
||||
if log::log_enabled!(log::Level::Error) {
|
||||
log::error!("{text}");
|
||||
|
||||
@@ -14,14 +14,14 @@ const DRIVER_HINT: &str = if cfg!(windows) {
|
||||
|
||||
#[test]
|
||||
fn the_message_carries_the_error_and_where_to_report_it() {
|
||||
let text = message(ERROR, false);
|
||||
let text = message(ERROR, false, None);
|
||||
assert!(text.contains(ERROR), "the error itself: {text}");
|
||||
assert!(text.contains("https://github.com/storytold/figcraft/issues"), "where to report it: {text}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn without_wgpu_backend_it_suggests_the_driver_and_another_graphics_api() {
|
||||
let text = message(ERROR, false);
|
||||
let text = message(ERROR, false, None);
|
||||
assert!(text.contains(tr(DRIVER_HINT)), "{text}");
|
||||
assert!(text.contains("WGPU_BACKEND"), "another graphics API to try: {text}");
|
||||
assert!(!text.contains(tr(BACKEND_HINT)), "{text}");
|
||||
@@ -30,13 +30,22 @@ fn without_wgpu_backend_it_suggests_the_driver_and_another_graphics_api() {
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn on_windows_it_shows_how_to_start_with_opengl() {
|
||||
let text = message(ERROR, false);
|
||||
let text = message(ERROR, false, None);
|
||||
assert!(text.contains("set WGPU_BACKEND=gl"), "a release build has no console to look this up from: {text}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_wgpu_backend_set_it_suggests_unsetting_it() {
|
||||
let text = message(ERROR, true);
|
||||
let text = message(ERROR, true, None);
|
||||
assert!(text.contains(tr(BACKEND_HINT)), "{text}");
|
||||
assert!(!text.contains(tr(DRIVER_HINT)), "the chosen backend is the likelier cause: {text}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn after_every_attempt_failed_it_names_what_was_tried_and_no_other_api() {
|
||||
let text = message(ERROR, false, Some("Direct3D 12, Vulkan, OpenGL"));
|
||||
assert!(text.contains(ERROR), "the last error: {text}");
|
||||
assert!(text.contains("Direct3D 12, Vulkan, OpenGL"), "what was tried: {text}");
|
||||
assert!(!text.contains(tr(DRIVER_HINT)), "every API was tried already: {text}");
|
||||
assert!(text.contains("https://github.com/storytold/figcraft/issues"), "where to report it: {text}");
|
||||
}
|
||||
|
||||
@@ -1558,6 +1558,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "حُدّث {name}"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "غالبًا ما يفيد تحديث تعريف الرسوميات. لاستخدام OpenGL وحده، شغّل FigCraft من موجه الأوامر باستخدام:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "غالبًا ما يفيد تحديث تعريف الرسوميات. يحدد WGPU_BACKEND (vulkan أو gl أو metal) واجهة رسوميات أخرى لتجربتها."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "جرّب FigCraft {apis}، ثم مرة أخرى بمتطلبات أقل على وحدة الرسوميات الموفّرة للطاقة. غالبًا ما يفيد تحديث تعريف الرسوميات."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "يرفع هذا الملف إلى خادم FigCraft ويبقي علامة التبويب متزامنة معه. تحصل على رابط للتحرير وآخر للعرض."
|
||||
"Uppercase" = "أحرف كبيرة"
|
||||
"Use a fixed value" = "استخدام قيمة ثابتة"
|
||||
|
||||
@@ -1505,6 +1505,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "{name} আপডেট করা হয়েছে"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "গ্রাফিক্স ড্রাইভার আপডেট করা প্রায়ই সহায়ক হয়। শুধুমাত্র OpenGL ব্যবহার করতে, কমান্ড প্রম্পট থেকে FigCraft শুরু করুন:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "গ্রাফিক্স ড্রাইভার আপডেট করা প্রায়ই সহায়ক হয়। WGPU_BACKEND (vulkan, gl, metal) চেষ্টা করার জন্য অন্য একটি গ্রাফিক্স API এর নাম বলে।"
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft {apis} চেষ্টা করেছে, তারপর শক্তি-সাশ্রয়ী GPU-তে কম চাহিদায় আবার চেষ্টা করেছে। গ্রাফিক্স ড্রাইভার আপডেট করা প্রায়ই সহায়ক হয়।"
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "এই ফাইলটি একটি FigCraft সার্ভারে আপলোড করে এবং এই ট্যাবটিকে এর সাথে সিঙ্ক করে রাখে। আপনি এডিট করার জন্য একটি লিংক এবং দেখার জন্য একটি লিংক পান।"
|
||||
"Uppercase" = "বড় হাতের অক্ষর"
|
||||
"Use a fixed value" = "একটি নির্দিষ্ট মান ব্যবহার করুন"
|
||||
|
||||
@@ -1435,6 +1435,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "{name} aktualisiert"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Oft hilft es, den Grafiktreiber zu aktualisieren. Um nur OpenGL zu verwenden, starten Sie FigCraft in der Eingabeaufforderung mit:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Oft hilft es, den Grafiktreiber zu aktualisieren. Mit WGPU_BACKEND (vulkan, gl, metal) lässt sich eine andere Grafik-API ausprobieren."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft hat {apis} versucht und dann noch einmal mit geringeren Anforderungen auf der stromsparenden GPU. Oft hilft es, den Grafiktreiber zu aktualisieren."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Lädt diese Datei auf einen FigCraft-Server hoch und hält diesen Tab mit ihm synchron. Sie erhalten einen Link zum Bearbeiten und einen zum Ansehen."
|
||||
"Uppercase" = "Großbuchstaben"
|
||||
"Use a fixed value" = "Festen Wert verwenden"
|
||||
|
||||
@@ -1569,6 +1569,7 @@ name = "English"
|
||||
"Updated {name}" = "Updated {name}"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view."
|
||||
"Uppercase" = "Uppercase"
|
||||
"Use a fixed value" = "Use a fixed value"
|
||||
|
||||
@@ -1547,6 +1547,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "{name} actualizado"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Actualizar el controlador gráfico suele ayudar. Para usar solo OpenGL, inicia FigCraft desde el Símbolo del sistema con:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Actualizar el controlador gráfico suele ayudar. WGPU_BACKEND (vulkan, gl, metal) indica otra API gráfica que probar."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft probó {apis} y luego otra vez con menos requisitos en la GPU de bajo consumo. Actualizar el controlador gráfico suele ayudar."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Sube este archivo a un servidor FigCraft y mantiene esta pestaña sincronizada con él. Obtienes un enlace para editar y otro para ver."
|
||||
"Uppercase" = "Mayúsculas"
|
||||
"Use a fixed value" = "Usar un valor fijo"
|
||||
|
||||
@@ -1510,6 +1510,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "Päivitetty: {name}"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Näytönohjainajurin päivittäminen auttaa usein. Käytä vain OpenGL:ää käynnistämällä FigCraft komentokehotteesta näin:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Näytönohjainajurin päivittäminen auttaa usein. WGPU_BACKEND (vulkan, gl, metal) määrittää toisen kokeiltavan grafiikkarajapinnan."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft kokeili {apis} ja sitten uudelleen pienemmillä vaatimuksilla virtaa säästävällä näytönohjaimella. Näytönohjainajurin päivittäminen auttaa usein."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Lataa tämän tiedoston FigCraft-palvelimelle ja pitää tämän välilehden synkronoituna sen kanssa. Saat muokkauslinkin ja katselulinkin."
|
||||
"Uppercase" = "Isot kirjaimet"
|
||||
"Use a fixed value" = "Käytä kiinteää arvoa"
|
||||
|
||||
@@ -1510,6 +1510,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "{name} mis à jour"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Mettre à jour le pilote graphique aide souvent. Pour n’utiliser qu’OpenGL, lancez FigCraft depuis l’invite de commandes avec :"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Mettre à jour le pilote graphique aide souvent. WGPU_BACKEND (vulkan, gl, metal) désigne une autre API graphique à essayer."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft a essayé {apis}, puis à nouveau avec des exigences réduites sur le GPU économe en énergie. Mettre à jour le pilote graphique aide souvent."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Envoie ce fichier sur un serveur FigCraft et garde cet onglet synchronisé avec lui. Vous obtenez un lien de modification et un lien de consultation."
|
||||
"Uppercase" = "Majuscules"
|
||||
"Use a fixed value" = "Utiliser une valeur fixe"
|
||||
|
||||
@@ -1452,6 +1452,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "Aggiornato {name}"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Aggiornare il driver grafico spesso aiuta. Per usare solo OpenGL, avvia FigCraft dal Prompt dei comandi con:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Aggiornare il driver grafico spesso aiuta. WGPU_BACKEND (vulkan, gl, metal) indica un'altra API grafica da provare."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft ha provato {apis}, poi di nuovo con requisiti ridotti sulla GPU a basso consumo. Aggiornare il driver grafico spesso aiuta."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Carica questo file su un server FigCraft e mantiene questa scheda sincronizzata con esso. Ricevi un link per modificare e un link per visualizzare."
|
||||
"Uppercase" = "Maiuscolo"
|
||||
"Use a fixed value" = "Usa un valore fisso"
|
||||
|
||||
@@ -1493,6 +1493,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "{name} を更新済み"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "グラフィックスドライバーの更新で解決することが多い。OpenGL だけを使うには、コマンドプロンプトから次のように FigCraft を起動:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "グラフィックスドライバーの更新で解決することが多い。WGPU_BACKEND(vulkan、gl、metal)で別のグラフィックス API を試せる。"
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft は {apis} を試し、さらに省電力 GPU で要件を下げて再試行した。グラフィックスドライバーの更新で解決することが多い。"
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "このファイルを FigCraft サーバーにアップロードし、このタブを同期した状態に保つ。編集用リンクと閲覧用リンクが発行される。"
|
||||
"Uppercase" = "大文字"
|
||||
"Use a fixed value" = "固定値を使用"
|
||||
|
||||
@@ -1528,6 +1528,7 @@ status = "draft — reviewed by a native speaker, awaiting a maintainer's approv
|
||||
"Updated {name}" = "განახლდა: {name}"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "ხშირად გრაფიკის დრაივერის განახლება შველის. მხოლოდ OpenGL-ის გამოსაყენებლად FigCraft გაუშვით ბრძანების სტრიქონიდან (Command Prompt) ასე:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "ხშირად გრაფიკის დრაივერის განახლება შველის. WGPU_BACKEND-ით (vulkan, gl, metal) შეგიძლიათ სხვა გრაფიკული API სცადოთ."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft-მა სცადა {apis}, შემდეგ კი ისევ, ნაკლები მოთხოვნებით, ენერგოდამზოგ GPU-ზე. ხშირად გრაფიკის დრაივერის განახლება შველის."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "ტვირთავს ამ ფაილს FigCraft-ის სერვერზე და ამ ჩანართს მასთან სინქრონში ინახავს. მიიღებთ რედაქტირებისა და ნახვის ბმულებს."
|
||||
"Uppercase" = "მთავრული"
|
||||
"Use a fixed value" = "ფიქსირებული მნიშვნელობის გამოყენება"
|
||||
|
||||
@@ -1426,6 +1426,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "{name} 업데이트됨"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "그래픽 드라이버를 업데이트하면 해결되는 경우가 많습니다. OpenGL만 사용하려면 명령 프롬프트에서 다음과 같이 FigCraft를 시작하세요:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "그래픽 드라이버를 업데이트하면 해결되는 경우가 많습니다. WGPU_BACKEND(vulkan, gl, metal)로 다른 그래픽 API를 시도할 수 있습니다."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft에서 {apis}을(를) 시도한 뒤 저전력 GPU에서 요구 사항을 낮춰 다시 시도했습니다. 그래픽 드라이버를 업데이트하면 해결되는 경우가 많습니다."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "이 파일을 FigCraft 서버에 업로드하고 이 탭을 동기화된 상태로 유지함. 편집 링크와 보기 링크가 생성됨."
|
||||
"Uppercase" = "대문자"
|
||||
"Use a fixed value" = "고정 값 사용"
|
||||
|
||||
@@ -1510,6 +1510,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "{name} bijgewerkt"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Het bijwerken van de grafische driver helpt vaak. Om alleen OpenGL te gebruiken, start je FigCraft vanaf de Opdrachtprompt met:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Het bijwerken van de grafische driver helpt vaak. WGPU_BACKEND (vulkan, gl, metal) noemt een andere grafische API om te proberen."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft heeft {apis} geprobeerd en daarna opnieuw met lagere eisen op de energiezuinige GPU. Het bijwerken van de grafische driver helpt vaak."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Uploadt dit bestand naar een FigCraft-server en houdt dit tabblad daarmee gesynchroniseerd. Je krijgt een link om te bewerken en een link om te bekijken."
|
||||
"Uppercase" = "Hoofdletters"
|
||||
"Use a fixed value" = "Gebruik een vaste waarde"
|
||||
|
||||
@@ -1519,6 +1519,7 @@ name = "Polski"
|
||||
"Updated {name}" = "Zaktualizowano {name}"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Aktualizacja sterownika graficznego często pomaga. Aby używać tylko OpenGL, uruchom FigCraft z wiersza poleceń za pomocą:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Aktualizacja sterownika graficznego często pomaga. Zmienna WGPU_BACKEND (vulkan, gl, metal) pozwala wybrać inne graficzne API do wypróbowania."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft wypróbował {apis}, a potem ponownie z niższymi wymaganiami na energooszczędnym GPU. Aktualizacja sterownika graficznego często pomaga."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Przesyła ten plik na serwer FigCraft i synchronizuje tę kartę na bieżąco. Otrzymujesz link do edycji oraz link do podglądu."
|
||||
"Uppercase" = "WIELKIE LITERY"
|
||||
"Use a fixed value" = "Użyj stałej wartości"
|
||||
|
||||
@@ -1424,6 +1424,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "{name} atualizado"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Atualizar o driver de vídeo costuma ajudar. Para usar só o OpenGL, abra o FigCraft pelo Prompt de Comando com:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Atualizar o driver de vídeo costuma ajudar. WGPU_BACKEND (vulkan, gl, metal) indica outra API gráfica para tentar."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "O FigCraft tentou {apis} e depois de novo com requisitos menores na GPU de baixo consumo. Atualizar o driver de vídeo costuma ajudar."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Envia este arquivo para um servidor FigCraft e mantém esta aba sincronizada com ele. Você recebe um link para editar e outro para visualizar."
|
||||
"Uppercase" = "Maiúsculas"
|
||||
"Use a fixed value" = "Usar um valor fixo"
|
||||
|
||||
@@ -1519,6 +1519,7 @@ name = "Русский"
|
||||
"Updated {name}" = "Обновлено: {name}"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Часто помогает обновление драйвера видеокарты. Чтобы использовать только OpenGL, запустите FigCraft из командной строки с параметром:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Часто помогает обновление драйвера видеокарты. Переменная WGPU_BACKEND (vulkan, gl, metal) задаёт другой графический API, который можно попробовать."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft попробовал {apis}, а затем ещё раз с пониженными требованиями на энергосберегающем GPU. Часто помогает обновление драйвера видеокарты."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Загружает файл на сервер FigCraft и синхронизирует эту вкладку с ним. Вы получите ссылки для редактирования и просмотра."
|
||||
"Uppercase" = "Прописные"
|
||||
"Use a fixed value" = "Использовать фиксированное значение"
|
||||
|
||||
@@ -1553,6 +1553,7 @@ name = "Svenska"
|
||||
"Updated {name}" = "{name} har uppdaterats"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Det hjälper ofta att uppdatera grafikdrivrutinen. Om du bara vill använda OpenGL startar du FigCraft från en kommandotolk med:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Det hjälper ofta att uppdatera grafikdrivrutinen. WGPU_BACKEND (vulkan, gl, metal) anger ett annat grafik-API att prova."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft provade {apis} och sedan igen med lägre krav på den strömsnåla grafikprocessorn. Det hjälper ofta att uppdatera grafikdrivrutinen."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Laddar upp filen till en FigCraft-server och håller fliken synkroniserad med den. Du får en länk för redigering och en för visning."
|
||||
"Uppercase" = "Versaler"
|
||||
"Use a fixed value" = "Använd ett fast värde"
|
||||
|
||||
@@ -1501,6 +1501,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "อัปเดต {name} แล้ว"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "การอัปเดตไดรเวอร์กราฟิกมักช่วยได้ หากต้องการใช้ OpenGL เท่านั้น ให้เริ่ม FigCraft จาก Command Prompt ด้วย:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "การอัปเดตไดรเวอร์กราฟิกมักช่วยได้ WGPU_BACKEND (vulkan, gl, metal) ระบุ API กราฟิกอื่นที่ลองใช้ได้"
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft ลอง {apis} แล้ว จากนั้นลองอีกครั้งด้วยข้อกำหนดที่ต่ำลงบน GPU แบบประหยัดพลังงาน การอัปเดตไดรเวอร์กราฟิกมักช่วยได้"
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "อัปโหลดไฟล์นี้ไปยังเซิร์ฟเวอร์ FigCraft และซิงก์แท็บนี้กับไฟล์ คุณจะได้ลิงก์แก้ไขและลิงก์ดู"
|
||||
"Uppercase" = "ตัวพิมพ์ใหญ่"
|
||||
"Use a fixed value" = "ใช้ค่าคงที่"
|
||||
|
||||
@@ -1453,6 +1453,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "Đã cập nhật {name}"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "Việc cập nhật trình điều khiển đồ họa thường sẽ khắc phục được lỗi. Để chỉ sử dụng OpenGL, hãy khởi chạy FigCraft từ Command Prompt bằng:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "Việc cập nhật trình điều khiển đồ họa thường sẽ khắc phục được lỗi. Biến WGPU_BACKEND (vulkan, gl, metal) cho phép thử một API đồ họa khác."
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft đã thử {apis}, rồi thử lại với yêu cầu thấp hơn trên GPU tiết kiệm điện. Việc cập nhật trình điều khiển đồ họa thường sẽ khắc phục được lỗi."
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Tải tệp này lên máy chủ FigCraft và giữ thẻ này đồng bộ với máy chủ. Bạn sẽ nhận được một liên kết chỉnh sửa và một liên kết xem."
|
||||
"Uppercase" = "Chữ hoa"
|
||||
"Use a fixed value" = "Dùng giá trị cố định"
|
||||
|
||||
@@ -1434,6 +1434,7 @@ status = "draft — needs native review"
|
||||
"Updated {name}" = "已更新 {name}"
|
||||
"Updating the graphics driver often helps. To use OpenGL only, start FigCraft from a Command Prompt with:" = "更新显卡驱动通常会有帮助。若只想使用 OpenGL,请在命令提示符中这样启动 FigCraft:"
|
||||
"Updating the graphics driver often helps. WGPU_BACKEND (vulkan, gl, metal) names another graphics API to try." = "更新显卡驱动通常会有帮助。可用 WGPU_BACKEND(vulkan、gl、metal)指定另一个图形 API 来尝试。"
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps." = "FigCraft 已尝试 {apis},然后在省电 GPU 上以较低要求再次尝试。更新显卡驱动通常会有帮助。"
|
||||
"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "将此文件上传到 FigCraft 服务器,并使此标签页与之保持同步。你会获得一个编辑链接和一个查看链接。"
|
||||
"Uppercase" = "大写"
|
||||
"Use a fixed value" = "使用固定值"
|
||||
|
||||
@@ -44,9 +44,15 @@ const ISSUES: &str = "https://github.com/storytold/figcraft/issues";
|
||||
|
||||
/// What the desktop app says when its window couldn't open with `error` (no frame was drawn: the
|
||||
/// language is the one [`i18n::set`] picked from the preferences). `backend_set`: `WGPU_BACKEND`
|
||||
/// named the graphics API, so the hint is to unset it rather than to try another one.
|
||||
pub fn window_failed(error: &str, backend_set: bool) -> String {
|
||||
let hint = if backend_set {
|
||||
/// named the graphics API, so the hint is to unset it rather than to try another one. `tried`: the
|
||||
/// graphics APIs the app already started again with, so naming another one wouldn't help.
|
||||
pub fn window_failed(error: &str, backend_set: bool, tried: Option<&str>) -> String {
|
||||
let hint = if let Some(apis) = tried {
|
||||
trf(
|
||||
"FigCraft tried {apis}, then again with lower requirements on the power-saving GPU. Updating the graphics driver often helps.",
|
||||
&[("apis", &apis)],
|
||||
)
|
||||
} else if backend_set {
|
||||
tr("WGPU_BACKEND is set and names the graphics API to use; unsetting it lets FigCraft choose.").to_string()
|
||||
} else if cfg!(windows) {
|
||||
// Direct3D 12 and OpenGL are both offered, but wgpu keeps the Direct3D 12 adapter it picked
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Hardware and platform parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-29 · **Change:** minor (large images on the canvas draw from a smaller copy, so two 16k images no longer lag (#1483); macOS: the Quit menu and ⌘Q ask about unsaved changes (#857); egui/eframe 0.37 dependency migration; hidden-window screenshot deadlines; zoom previews; hidden windows answer control requests; macOS file opening, the Windows start-up message, Linux packaging; copy and paste in the browser; Windows QA pass; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; pen pressure for the Pencil and brushes on Windows and from browser styluses; macOS: quitting ends the process once everything is kept, avoiding the Touch Bar teardown crash (#781); Linux and BSD clipboard: images under Wayland, reads time out; minimised windows under Wayland; window controls on Wayland without server decorations; canvas textures on device pixels; copy and paste in the browser; Windows QA pass; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; pen pressure for the Pencil and brushes on Windows and from browser styluses) · **Target:** Figma Design (desktop app 126.10.7)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-30 · **Change:** minor (the window starts on other graphics when its first ones fail before the first frame: Direct3D 12 compiles with FXC and no device compiles wgpu's indirect-validation shaders, then Vulkan, OpenGL and lower requirements on the power-saving GPU (#965, #1438); large images on the canvas draw from a smaller copy, so two 16k images no longer lag (#1483); macOS: the Quit menu and ⌘Q ask about unsaved changes (#857); egui/eframe 0.37 dependency migration; hidden-window screenshot deadlines; zoom previews; hidden windows answer control requests; macOS file opening, the Windows start-up message, Linux packaging; copy and paste in the browser; Windows QA pass; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; pen pressure for the Pencil and brushes on Windows and from browser styluses; macOS: quitting ends the process once everything is kept, avoiding the Touch Bar teardown crash (#781); Linux and BSD clipboard: images under Wayland, reads time out; minimised windows under Wayland; window controls on Wayland without server decorations; canvas textures on device pixels; copy and paste in the browser; Windows QA pass; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; pen pressure for the Pencil and brushes on Windows and from browser styluses) · **Target:** Figma Design (desktop app 126.10.7)
|
||||
|
||||
How FigCraft uses hardware and which platforms it runs on, against Figma. Figma's desktop app is
|
||||
Electron with a GPU helper process; its canvas renders on the GPU (WebGL, with WebGPU rolling out
|
||||
@@ -40,7 +40,7 @@ Nothing has been timed against Figma on the same document (needs `.fig` import).
|
||||
| Platform | Figma | FigCraft | Notes |
|
||||
|---|---|---|---|
|
||||
| macOS | desktop app (Intel and Apple silicon) | universal `.dmg`, signed and notarized (v0.2.0) | where nearly all QA happens. Quitting ends the process once the preferences, recovery copies and live sessions are taken care of: the window's teardown crashed on Macs with a Touch Bar (#781); the Quit menu and ⌘Q ask about unsaved changes like the main window's close button (#857), and like it close the main window only: with several windows open, each quit closes one; a quit that never asked — killed or crashed — still leaves recovery copies for the next start |
|
||||
| Windows x64 / arm64 | desktop app | `.msi` and portable zip for x64, x86 and arm64 | built in CI; Windows: QA pass done 2026-10-11, findings in PR |
|
||||
| Windows x64 / arm64 | desktop app | `.msi` and portable zip for x64, x86 and arm64 | built in CI; Windows: QA pass done 2026-10-11, findings in PR. Draws with Direct3D 12 (FXC shaders) or OpenGL; when the graphics fail before the first frame it starts again on Vulkan, then OpenGL, then the power-saving GPU with lower limits (#965) |
|
||||
| Linux | none (browser only) | AppImage, `.deb`, `.rpm`, Flatpak, tarball, x86_64 and aarch64; riscv64 tarball | CI tests run here. Clipboard (also the BSDs): reads run on a helper thread that gives up after 2 s, and images copied in other apps paste under Wayland through the data-control protocol (#693). Under Wayland the app can't tell its window is minimised, so control requests that need a drawn frame may still wait there (#720). None of this run on a Linux desktop here. On a Wayland compositor without server-side decorations (GNOME) the window gets winit's Adwaita controls (#592: checked by its author in a GNOME Wayland session; KDE not run) |
|
||||
| FreeBSD | none | tarball | |
|
||||
| Web | primary (figma.com) | `figcraft-web` (WASM, WebGPU/WebGL2), web zip | no accessibility tree; file access through the browser; copy and paste through the system clipboard (copies written from the copy event, pastes read from the paste event; no pasting images from other apps) |
|
||||
@@ -50,6 +50,7 @@ Nothing has been timed against Figma on the same document (needs `.fig` import).
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-30 | minor | M1, #965, #1438: "Parent device is lost" at start. Both reports lost the device while wgpu compiled its indirect-call validation shaders, which it builds with every device: on Windows Direct3D 12 took another program's `dxcompiler.dll` from the search path and failed without its `dxil.dll`; Intel Macs' Metal compiler aborts on them. Direct3D 12 now compiles with FXC (part of Windows; `WGPU_DX12_COMPILER` still chooses), and the validation shaders are left out (FigCraft draws nothing indirectly; `WGPU_VALIDATION_INDIRECT_CALL` turns them back on). When the graphics still fail before the window's first frame, the app starts itself again with the next attempt, from a fixed list: on Windows Direct3D 12/OpenGL, Vulkan, OpenGL, then the first again on the power-saving GPU with WebGL2-level device limits; on Linux and FreeBSD the defaults, OpenGL, then lower requirements; on macOS the defaults, then lower requirements. The last failure's message names what was tried; the log names the backend and GPU that drew. `WGPU_BACKEND` is still the one attempt. A restarted app waits up to 5 s for its `--control` port. Checked on Windows 11 with a local build that made the first one, two, three and then all four attempts fail: each fallback opened the window (Vulkan, OpenGL, Direct3D 12 on the integrated GPU) and four failures ended with the message. Not run on the reporters' machines or on macOS. Tests: `graphics_tests`, `startup_tests`. Numbers unchanged |
|
||||
| 2026-10-29 | minor | Large images on the canvas (#1483): an image shown smaller than its pixels draws on the canvas from a copy scaled down to 4096 or 8192 px on its longer side, the smallest that holds what the screen shows (`figcraft_render::images::shown`), cached beside the image itself in the same 512 MB budget. Two 16 000 × 10 000 images took 640 MB each and evicted each other on every frame that drew both; as copies they take 80 MB together zoomed out and 336 MB at 8192. Moving one over the other went from about 2.1 s a frame to 11 ms zoomed to fit and 15 ms at 30% in the app (debug build). Closer than that, and for tiles, the canvas draws from the image itself, so 100% and closer stay sharp; a view of both at 100% still takes about 2 s a frame, and an image with shadows still decodes in full for them. Exports, thumbnails and every other render are unchanged. Tests: `large_image_tests`. Numbers unchanged |
|
||||
| 2026-10-29 | minor | macOS quit confirmation (#857): winit's default Quit menu item (⌘Q) ended the process through AppKit's `terminate:` at once, so a dirty app died without asking anything. The app now installs the craft-libs app menu (craft-menu): its Quit sends the main window a close request — the same path as its close button — so the unsaved-changes question asks before anything is lost (`windows_tests::a_quit_with_unsaved_changes_waits_for_the_answer`; checked in the app on macOS). Like the close button it closes the main window only, so with several windows open each quit closes one. A second quit before the window draws again (two quits while it is minimised, when only the app's logic runs) counts once: before, it could quit the app over another window's unsaved changes (`windows_tests::quitting_twice_before_the_window_draws_closes_it_once`). Numbers unchanged |
|
||||
| 2026-10-25 | minor | egui/eframe 0.37 dependency migration: the workspace pins the egui family at 0.37, and the wide-gamut and several-windows rows now cite 0.37. The limitations are unchanged: egui-wgpu 0.37 offers no surface colour-space option, so the screen still shows sRGB; eframe 0.37 still gives only the main window an AccessKit adapter, so files stay tabs while a screen reader runs. Numbers unchanged |
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
# FigCraft roadmap: milestones and what's next
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-29 · **Change:** minor (Thai, Lao, Khmer and Burmese lines break between words (#1219); an AI agent attached over MCP shows where it works (#1416); a manual overlay's position can be dragged on the canvas (#405); one name for live sessions and comments: Preferences ▸ Your name, in the Join dialog too, kept in the browser, and a rename shows at once in the sessions you are in (#1380); requests that change a server's files have a budget per address, the server key's too, and join codes are found by their hash (#1075); the Join dialog shows a join from the click until its file is ready, with Cancel, and a failed join with Try again (#1381); requests that change a server's files have a budget per address, the server key's too, and join codes are found by their hash (#1075); SVG import draws each pattern tile once per scale, within the layer budget (#1080); Vietnamese letters typed with combining marks (decomposed) take one font that has every mark, instead of missing-glyph boxes beside the letter (#1478); comment.list reports each message's @mentions (#1415); M8: mask groups draw only over the area they can paint, and nested isolated layers share one memory budget (#1061); M2: components can't contain their own instances, directly or through others; `edit.duplicate` returns an id map with `map: true` (#995); the batches kept for reconnecting apps are bounded in bytes by default, and `figcraft-server` takes `max_log_mb` (#1069); MCP and control calls name their document (#998); sibling placement in live sessions searches instead of counting (#1065); an AI agent attached over MCP comments under its own name, with an Agent label (#1414); the instance-swap menus have a search field (#1338); a refused batch's fixes take time linear in the batch (#1063); the connector tool shows the connector it will make while dragging, ends snapping to attach points (#1387); large images on the canvas draw from a smaller copy, so two 16k images no longer lag (#1483); a file's JPEG thumbnail can't crash the home screen (#1491's progressive JPEG check, now shared); `.fig` import: library text styles linked by asset key, and override rows restating a nested level (#1234); `.fig` import keeps the file's saved line breaks while the text and its box are unedited (#1236); Download a missing free font from the Missing fonts window (`font.download`, #754); the web build's wasm back under its 24 MiB gate (#1472); shadows of images with transparent pixels follow their alpha (#1289); Crop image: Object ▸ Crop image, the right-click menu and the image fill row enter crop mode (`image.crop`, #1464); M0.9: optional batch continue-on-error with per-step outcomes (#1386); Alt pressed during a drag duplicates (#1385); variables in paint props (M3, #1000); finding layers without selecting them (M0.9, part of #996); the attached MCP server reads the app's replies with a 512 MiB bound (#1113); a failed batch keeps the undo steps it undid (#1410); Arabic draft catalog; Android Vector Drawable export; a selected unfilled shape drags over a filled layer below it (#1370); text truncation fills the last line, survives resizing, and auto height takes Max lines (#1375, #1376, #1377); M11 widget fields and property menus; M12 Boards: timer and voting; Installed weights of an embedded font family (#1246); `.sketch` import, best effort; Adobe Fonts found on macOS (#298); M12 Boards; community fixes in #507, #510, #529 and #530: auto-save, a text box by dragging, batch step references, removing someone from a live session, Russian and Turkish catalogs reviewed, Czech and Indonesian drafts, the Japanese draft completed, zoom previews, the web clipboard, Alt-drag snapping and Cmd+D repeat, recovery copies, crash and rollback fixes, colour emoji, `.fig` import fixes; earlier changes are in the revision history below) · **Target:** Figma Design (desktop app 126.10.7)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-29 · **Change:** minor (the window starts on other graphics when its first ones fail before the first frame: Direct3D 12 compiles with FXC and no device compiles wgpu's indirect-validation shaders, then Vulkan, OpenGL and lower requirements on the power-saving GPU (#965, #1438); Thai, Lao, Khmer and Burmese lines break between words (#1219); an AI agent attached over MCP shows where it works (#1416); a manual overlay's position can be dragged on the canvas (#405); one name for live sessions and comments: Preferences ▸ Your name, in the Join dialog too, kept in the browser, and a rename shows at once in the sessions you are in (#1380); requests that change a server's files have a budget per address, the server key's too, and join codes are found by their hash (#1075); the Join dialog shows a join from the click until its file is ready, with Cancel, and a failed join with Try again (#1381); requests that change a server's files have a budget per address, the server key's too, and join codes are found by their hash (#1075); SVG import draws each pattern tile once per scale, within the layer budget (#1080); Vietnamese letters typed with combining marks (decomposed) take one font that has every mark, instead of missing-glyph boxes beside the letter (#1478); comment.list reports each message's @mentions (#1415); M8: mask groups draw only over the area they can paint, and nested isolated layers share one memory budget (#1061); M2: components can't contain their own instances, directly or through others; `edit.duplicate` returns an id map with `map: true` (#995); the batches kept for reconnecting apps are bounded in bytes by default, and `figcraft-server` takes `max_log_mb` (#1069); MCP and control calls name their document (#998); sibling placement in live sessions searches instead of counting (#1065); an AI agent attached over MCP comments under its own name, with an Agent label (#1414); the instance-swap menus have a search field (#1338); a refused batch's fixes take time linear in the batch (#1063); the connector tool shows the connector it will make while dragging, ends snapping to attach points (#1387); large images on the canvas draw from a smaller copy, so two 16k images no longer lag (#1483); a file's JPEG thumbnail can't crash the home screen (#1491's progressive JPEG check, now shared); `.fig` import: library text styles linked by asset key, and override rows restating a nested level (#1234); `.fig` import keeps the file's saved line breaks while the text and its box are unedited (#1236); Download a missing free font from the Missing fonts window (`font.download`, #754); the web build's wasm back under its 24 MiB gate (#1472); shadows of images with transparent pixels follow their alpha (#1289); Crop image: Object ▸ Crop image, the right-click menu and the image fill row enter crop mode (`image.crop`, #1464); M0.9: optional batch continue-on-error with per-step outcomes (#1386); Alt pressed during a drag duplicates (#1385); variables in paint props (M3, #1000); finding layers without selecting them (M0.9, part of #996); the attached MCP server reads the app's replies with a 512 MiB bound (#1113); a failed batch keeps the undo steps it undid (#1410); Arabic draft catalog; Android Vector Drawable export; a selected unfilled shape drags over a filled layer below it (#1370); text truncation fills the last line, survives resizing, and auto height takes Max lines (#1375, #1376, #1377); M11 widget fields and property menus; M12 Boards: timer and voting; Installed weights of an embedded font family (#1246); `.sketch` import, best effort; Adobe Fonts found on macOS (#298); M12 Boards; community fixes in #507, #510, #529 and #530: auto-save, a text box by dragging, batch step references, removing someone from a live session, Russian and Turkish catalogs reviewed, Czech and Indonesian drafts, the Japanese draft completed, zoom previews, the web clipboard, Alt-drag snapping and Cmd+D repeat, recovery copies, crash and rollback fixes, colour emoji, `.fig` import fixes; earlier changes are in the revision history below) · **Target:** Figma Design (desktop app 126.10.7)
|
||||
|
||||
Forward-looking plan. Where FigCraft stands today: [`ROADMAP.md`](../ROADMAP.md) and
|
||||
[`target-app-parity.md`](target-app-parity.md); the full ranked list: [`gaps.md`](gaps.md). Hours
|
||||
@@ -75,6 +75,7 @@ blockers.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-30 | minor | M1, #965, #1438: "Parent device is lost" at start: Direct3D 12 compiles with FXC, no device compiles wgpu's indirect-validation shaders, and when the graphics still fail before the first frame the app starts again with the next of a fixed list of attempts (Windows: Vulkan, OpenGL; Linux and FreeBSD: OpenGL; then lower requirements on the power-saving GPU); the last failure names what was tried. Tests: `graphics_tests`, `startup_tests` |
|
||||
| 2026-10-29 | minor | M4, #1219: Thai, Lao, Khmer and Burmese text, written without spaces between words, wraps between words instead of where a line overflows (`พัฒนาซอ|ฟต์แวร์` → `พัฒนา|ซอฟต์แวร์`). `figcraft_text::breaks::word_breaks` finds the word starts inside each run of these scripts with ICU4X's line segmenter (`icu_segmenter` 2.3): its dictionaries on desktop, its LSTM models in the browser (about 0.39 MB of wasm instead of 1.86 MB, which the 24 MiB gate can't take; they split a few compounds the dictionaries keep whole). Where a run meets spaces, Latin or CJK the UAX #14 rules decide as before, and a word wider than its box still takes #1400's emergency break. Text without these scripts does no extra work. Grade unchanged. Evidence: `breaks::tests::thai_lines_break_between_words`, `lao_khmer_and_burmese_lines_break_between_words`, `word_breaks_survive_junk`, `behaviour_tests::thai_lines_break_between_words` (fails on main). |
|
||||
| 2026-10-30 | minor | M8, #1416: an attached MCP agent's presence (avatar in the people row, outline and name tag on the layers it last edited or selected), set by `Session::execute_as` and gone 8 seconds after its last command; display only, never synced. Tests: `agent_presence_tests` (engine), `live::agent` (ui-egui) |
|
||||
| 2026-10-30 | minor | M5, #405: a manual overlay's ghost shows on the canvas while its Open overlay action is open in the interaction editor, and dragging it sets the action's offset from the layer that opens it (X and Y follow, one undo step). Tests: `overlay_ghost` (ui-egui) |
|
||||
|
||||
Reference in New Issue
Block a user