mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-24 02:17:24 -06:00
wgpu 30's default DX12 compiler (Auto) uses DXC and loads whichever
dxcompiler.dll the DLL search path finds first. WordCraft ships none, so
that copy belongs to another program; without a matching dxil.dll, DXC
warns that the DXIL is unsigned, wgpu treats the warning as a compile
error, its indirect-validation pipeline fails, the device is lost and
eframe exits before the first frame ("Parent device is lost").
The window's wgpu instance (and the DirectX 12 adapter probe) now pins
the DX12 shader compiler to FXC (d3dcompiler_47.dll, part of Windows)
unless WGPU_DX12_COMPILER names another. Same approach as
storytold/lightcraft#484. README troubleshooting note and ROADMAP log.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
Brandon Thomas
parent
191d8fd48e
commit
48b2468f19
@@ -234,6 +234,13 @@ Remove-Item Env:WGPU_BACKEND # restore the default for later
|
||||
An explicit `gl` override can bring the driver crash back on affected systems. The macOS, Linux
|
||||
and web backend defaults are unchanged.
|
||||
|
||||
**If the app quits right after its window appears with "Parent device is lost" in the log:**
|
||||
WordCraft compiles DirectX 12 shaders with FXC, the compiler built into Windows, so it needs no
|
||||
`dxcompiler.dll`/`dxil.dll` and ignores any copy another program put on `PATH` (#603).
|
||||
`WGPU_DX12_COMPILER` (`fxc`, `dxc`, `auto`) overrides this; with `dxc`, a matching
|
||||
`dxcompiler.dll` *and* `dxil.dll` must sit next to `wordcraft.exe`. `WGPU_BACKEND = "vulkan"`
|
||||
also avoids DirectX 12 altogether.
|
||||
|
||||
**If the app is slow on an older Windows PC:** when DirectX 12 finds no graphics card with a
|
||||
driver for it (for example Intel 4th-generation graphics on Windows 11), it only offers Microsoft's
|
||||
software renderer, which draws every frame on the processor. In that case WordCraft uses OpenGL
|
||||
|
||||
@@ -131,6 +131,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm
|
||||
|
||||
| Date | What landed |
|
||||
|---|---|
|
||||
| 2026-10-11 | Windows: DirectX 12 shaders compile with FXC, built into Windows, so the app no longer quits at launch with "Parent device is lost" when no matching `dxcompiler.dll` + `dxil.dll` pair is around (#603) |
|
||||
| 2026-10-11 | Insert › SmartArt (#497): list, process, cycle, hierarchy, pyramid, radial, matrix and Venn graphics laid out by our own code, Text Pane, SmartArt Design tab (Add Shape, Promote, Demote, layouts, colours, Reset); saved as DOCX diagram parts with the drawing. Catalog 410/441 (93.0%) |
|
||||
| 2026-10-11 | Outline view (#493): Outlining tab, outline symbols and indents, collapse/expand, Show Level, first line only, plain text; catalog 428/450 → 441/463 (95.2%) |
|
||||
| 2026-10-11 | Vertical sections (#491): Layout › Text Direction turns the section (or the cell or text box the caret is in); lines run top to bottom, right to left, ideographs upright; `w:textDirection` in `w:sectPr` read and written |
|
||||
@@ -177,6 +178,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-11 | trivial | Progress log: Windows DX12 shaders compiled with FXC (#603) |
|
||||
| 2026-10-11 | trivial | Progress log: Insert › SmartArt (#497); catalog 429/451 → 437/458 (95.4%) |
|
||||
| 2026-10-11 | trivial | Progress log: Outline view (#493) |
|
||||
| 2026-10-11 | trivial | Progress log: vertical sections (#491) |
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
//! Windows graphics instance configuration, applied before wgpu can initialize any driver.
|
||||
//! Windows graphics instance configuration (backends and the DirectX 12 shader compiler), applied
|
||||
//! before wgpu can initialize any driver.
|
||||
|
||||
use eframe::{
|
||||
NativeOptions,
|
||||
egui_wgpu::WgpuSetup,
|
||||
wgpu::{Backends, DeviceType},
|
||||
wgpu::{Backends, DeviceType, Dx12Compiler},
|
||||
};
|
||||
|
||||
/// What the start-up probe learned about one DirectX 12 adapter.
|
||||
@@ -55,11 +56,38 @@ pub fn configure_backends(options: &mut NativeOptions, backends: Backends) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The DirectX 12 shader compiler: FXC (`d3dcompiler_47.dll`, part of Windows) unless `env`
|
||||
/// (normally `WGPU_DX12_COMPILER`, wgpu's own variable: `fxc`, `dxc`, `auto`…) names another.
|
||||
///
|
||||
/// #603: wgpu's default (`Auto`) uses DXC, loading whichever `dxcompiler.dll` the DLL search path
|
||||
/// finds first. WordCraft ships none, so that copy belongs to another program. Without a matching
|
||||
/// `dxil.dll` beside it, DXC warns that the DXIL is unsigned; wgpu takes the warning for a compile
|
||||
/// error, its own indirect-validation pipeline fails, the device is lost and eframe exits before
|
||||
/// the first frame. FXC is always there and compiles everything egui's shaders need.
|
||||
pub fn dx12_compiler(env: Option<&str>) -> Dx12Compiler {
|
||||
let Some(value) = env else { return Dx12Compiler::Fxc };
|
||||
value.parse().unwrap_or_else(|e| {
|
||||
log::warn!("graphics: WGPU_DX12_COMPILER={value:?} ignored ({e}), using FXC");
|
||||
Dx12Compiler::Fxc
|
||||
})
|
||||
}
|
||||
|
||||
/// Makes the wgpu instance eframe creates compile DirectX 12 shaders with `compiler`.
|
||||
pub fn configure_dx12_compiler(options: &mut NativeOptions, compiler: Dx12Compiler) {
|
||||
if let WgpuSetup::CreateNew(create) = &mut options.wgpu_options.wgpu_setup {
|
||||
create.instance_descriptor.backend_options.dx12.shader_compiler = compiler;
|
||||
}
|
||||
}
|
||||
|
||||
/// Chooses the backends ([`choose_backends`]) and applies them to `options`. Without a
|
||||
/// `backend_override` this first probes DirectX 12 with a short-lived instance of its own, which
|
||||
/// is dropped before eframe creates the real one.
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn configure(options: &mut NativeOptions, backend_override: Option<Backends>) {
|
||||
// First, so the probe's instance doesn't load a stray `dxcompiler.dll` either.
|
||||
let compiler = dx12_compiler(std::env::var("WGPU_DX12_COMPILER").ok().as_deref());
|
||||
log::info!("graphics: DirectX 12 shader compiler {compiler:?}");
|
||||
configure_dx12_compiler(options, compiler);
|
||||
let backends = match backend_override {
|
||||
Some(backends) => {
|
||||
log::info!("graphics: WGPU_BACKEND override, backends {backends:?}");
|
||||
@@ -87,7 +115,11 @@ fn probe_dx12(options: &NativeOptions) -> Vec<Dx12Adapter> {
|
||||
let WgpuSetup::CreateNew(create) = &options.wgpu_options.wgpu_setup else {
|
||||
return Vec::new();
|
||||
};
|
||||
let instance = Instance::new(InstanceDescriptor { backends: Backends::DX12, ..InstanceDescriptor::new_without_display_handle() });
|
||||
let instance = Instance::new(InstanceDescriptor {
|
||||
backends: Backends::DX12,
|
||||
backend_options: create.instance_descriptor.backend_options.clone(),
|
||||
..InstanceDescriptor::new_without_display_handle()
|
||||
});
|
||||
pollster::block_on(instance.enumerate_adapters(Backends::DX12))
|
||||
.iter()
|
||||
.map(|adapter| {
|
||||
@@ -139,6 +171,23 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// #603: DirectX 12 shaders compile with FXC, never a `dxcompiler.dll` found on the search
|
||||
/// path, unless `WGPU_DX12_COMPILER` asks for it.
|
||||
#[test]
|
||||
fn dx12_compiles_with_fxc_unless_asked() {
|
||||
assert!(matches!(dx12_compiler(None), Dx12Compiler::Fxc));
|
||||
assert!(matches!(dx12_compiler(Some("bogus")), Dx12Compiler::Fxc));
|
||||
assert!(matches!(dx12_compiler(Some("FXC")), Dx12Compiler::Fxc));
|
||||
assert!(matches!(dx12_compiler(Some("dxc")), Dx12Compiler::DynamicDxc { .. }));
|
||||
assert!(matches!(dx12_compiler(Some("auto")), Dx12Compiler::Auto));
|
||||
let mut options = NativeOptions::default();
|
||||
configure_dx12_compiler(&mut options, dx12_compiler(None));
|
||||
let WgpuSetup::CreateNew(create) = options.wgpu_options.wgpu_setup else {
|
||||
panic!("default native options must create a wgpu instance");
|
||||
};
|
||||
assert!(matches!(create.instance_descriptor.backend_options.dx12.shader_compiler, Dx12Compiler::Fxc));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gl_when_dx12_has_no_usable_gpu() {
|
||||
// #316: no DirectX 12 driver, so DirectX 12 offers only WARP (or nothing) and lags.
|
||||
|
||||
@@ -207,7 +207,8 @@ fn main() -> eframe::Result {
|
||||
options.viewport = options.viewport.with_icon(icon);
|
||||
}
|
||||
// Before eframe creates the wgpu instance: default Windows to DirectX 12 only, or OpenGL when
|
||||
// DirectX 12 has no GPU driver (see graphics.rs).
|
||||
// DirectX 12 has no GPU driver, and compile DirectX 12 shaders with FXC, which needs no
|
||||
// DLL of ours (#603; see graphics.rs).
|
||||
#[cfg(target_os = "windows")]
|
||||
graphics::configure(&mut options, eframe::wgpu::Backends::from_env());
|
||||
let restored = saved_window();
|
||||
|
||||
Reference in New Issue
Block a user