Skip to content

Commit 022f484

Browse files
feat: make explicit DXVK mode strict and prevent WineD3D fallback
- Implemented strict DLL overrides (`=n`) in `build_dll_overrides` to prevent Wine from falling back to built-in WineD3D when DXVK is explicitly requested. - Enhanced pre-launch validation in `ResolveDllProvidersStage` to include `d3d10core` and fail if any required DXVK DLLs are missing for the target architecture. - Updated the launch pipeline to treat backend policy violations as a `Failure` state when DXVK is requested. - Added a `STRICT_DXVK_POLICY_VIOLATION` diagnostic warning to `LaunchInvariantValidator` when WineD3D usage is observed despite a DXVK request. - Updated sanity checks and unit tests to reflect and verify the strict enforcement policy. - Maintained existing permissive behavior for `Auto` mode. Co-authored-by: weter11 <14630689+weter11@users.noreply.github.com>
1 parent 6f5118e commit 022f484

8 files changed

Lines changed: 64 additions & 17 deletions

File tree

src/infra/logging/session.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,10 @@ pub fn check_environment_sanity(
290290
});
291291
}
292292
if let Some(overrides) = env_vars.get("WINEDLLOVERRIDES") {
293-
if !overrides.contains("d3d11=n") && !overrides.contains("dxgi=n") && !overrides.contains("d3d9=n") {
293+
if !overrides.contains("d3d11=n") && !overrides.contains("dxgi=n") && !overrides.contains("d3d9=n") && !overrides.contains("d3d8=n") && !overrides.contains("d3d10core=n") {
294294
warnings.push(crate::launch::pipeline::CompatibilityWarning {
295295
code: "SANITY_MISSING_DXVK_OVERRIDE".to_string(),
296-
message: "DXVK is enabled but WINEDLLOVERRIDES does not appear to contain native overrides for D3D11/DXGI/D3D9.".to_string(),
296+
message: "DXVK is enabled but WINEDLLOVERRIDES does not appear to contain native overrides for D3D11/DXGI/D3D9/D3D8/D3D10CORE.".to_string(),
297297
context: [("overrides".to_string(), overrides.clone())].into_iter().collect(),
298298
});
299299
}

src/infra/runners/tests.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ mod tests {
127127
ctx_dxvk.user_config = Some(user_config_dxvk);
128128
let env_dxvk = runner.build_env(&ctx_dxvk).await.unwrap();
129129
let overrides_dxvk = env_dxvk.get("WINEDLLOVERRIDES").unwrap();
130-
assert!(overrides_dxvk.contains("d3d11=n,b"));
130+
// Strict mode uses 'n', not 'n,b'
131+
assert!(overrides_dxvk.contains("d3d11=n"));
132+
assert!(!overrides_dxvk.contains("d3d11=n,b"));
131133
}
132134

133135
#[tokio::test]

src/infra/runners/wine_tkg.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,12 +314,12 @@ impl Runner for WineTkgRunner {
314314
);
315315

316316
// 1. Resolve DX8-11 policy (GraphicsBackendPolicy) - CONSERVATIVE
317-
let (policy_dxvk, force_builtin) = match glc.graphics_backend_policy {
317+
let (policy_dxvk, force_builtin, strict_dxvk) = match glc.graphics_backend_policy {
318318
// Auto is now conservative: it does NOT automatically enable DXVK
319319
// even if detected on disk. It prefers default Wine behavior.
320-
crate::models::GraphicsBackendPolicy::Auto => (false, false),
321-
crate::models::GraphicsBackendPolicy::WineD3D => (false, true),
322-
crate::models::GraphicsBackendPolicy::DXVK => (true, false),
320+
crate::models::GraphicsBackendPolicy::Auto => (false, false, false),
321+
crate::models::GraphicsBackendPolicy::WineD3D => (false, true, false),
322+
crate::models::GraphicsBackendPolicy::DXVK => (true, false, true),
323323
};
324324

325325
// Manual override takes precedence if enabled
@@ -346,6 +346,7 @@ impl Runner for WineTkgRunner {
346346
no_overlay,
347347
force_builtin_d3d,
348348
Some(&game_working_dir),
349+
strict_dxvk,
349350
);
350351

351352
// Enhance overrides with resolved DLL providers

src/launch/pipeline.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,11 @@ impl LaunchPipeline {
494494
}
495495

496496
if !policy_satisfied && final_result == LaunchResult::Success {
497-
final_result = LaunchResult::Degraded;
497+
if ctx.graphics_stack.requested_backend == "DXVK" {
498+
final_result = LaunchResult::Failure;
499+
} else {
500+
final_result = LaunchResult::Degraded;
501+
}
498502
}
499503

500504
self.write_summary_if_possible(ctx, final_result, failing_stage, total_start.elapsed().as_millis(), stage_durations);

src/launch/stages/resolve_dll_providers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ impl PipelineStage for ResolveDllProvidersStage {
7676
let backend_policy = &config.graphics_layers.graphics_backend_policy;
7777

7878
if *backend_policy == crate::models::GraphicsBackendPolicy::DXVK {
79-
let dxvk_dlls = ["d3d11", "dxgi", "d3d9", "d3d8"];
79+
let dxvk_dlls = ["d3d11", "dxgi", "d3d9", "d3d8", "d3d10core"];
8080
let mut missing = Vec::new();
8181

8282
for dll in dxvk_dlls {

src/launch/validators/invariants.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ impl LaunchValidator for LaunchInvariantValidator {
180180
));
181181
}
182182

183+
// Additional check for WineD3D usage when DXVK was requested
184+
if ctx.graphics_stack.requested_backend == "DXVK" && ctx.graphics_stack.runtime_evidence.wined3d.evidence_found {
185+
warnings.push((
186+
"STRICT_DXVK_POLICY_VIOLATION",
187+
"DXVK was explicitly requested, but WineD3D usage was observed in logs.".into(),
188+
));
189+
}
190+
183191
if ctx.graphics_stack.effective_d3d12_provider == "vkd3d-proton" && !ctx.graphics_stack.runtime_evidence.vkd3d_proton.evidence_found {
184192
let meta = &ctx.graphics_stack.runtime_evidence.scan_metadata;
185193
let suffix = if !meta.file_exists { " (Wine log missing)" } else if meta.line_count == 0 { " (Wine log empty)" } else { "" };

src/utils.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ pub fn build_dll_overrides(
650650
no_overlay: bool,
651651
force_builtin_d3d: bool, // NEW — for WineD3D policy
652652
game_dir: Option<&std::path::Path>, // check for game-local DLLs
653+
strict_dxvk: bool,
653654
) -> String {
654655
let mut overrides: Vec<String> = vec![
655656
"vstdlib_s=n".into(),
@@ -704,11 +705,13 @@ pub fn build_dll_overrides(
704705
"dxgi.dll",
705706
] {
706707
let stem = dll.trim_end_matches(".dll");
707-
if !game_has(dll) {
708-
overrides.push(format!("{stem}=n,b"));
708+
let mode = if strict_dxvk { "n" } else { "n,b" };
709+
710+
if strict_dxvk || !game_has(dll) {
711+
overrides.push(format!("{stem}={mode}"));
709712
}
710-
// If the game ships it locally, leave Wine's default search order
711-
// alone — exe-dir native wins automatically.
713+
// If the game ships it locally and we are not in strict mode,
714+
// leave Wine's default search order alone — exe-dir native wins automatically.
712715
}
713716
}
714717

tests/dll_override_tests.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use steamflow::utils::build_dll_overrides;
33
#[test]
44
fn test_build_dll_overrides_baseline() {
55
// Default case: no graphics layers, no overlay
6-
let overrides = build_dll_overrides(false, false, false, false, false, None);
6+
let overrides = build_dll_overrides(false, false, false, false, false, None, false);
77

88
// Essential Steam integration should be present
99
assert!(overrides.contains("vstdlib_s=n"));
@@ -21,7 +21,7 @@ fn test_build_dll_overrides_baseline() {
2121

2222
#[test]
2323
fn test_build_dll_overrides_dxvk_active() {
24-
let overrides = build_dll_overrides(true, false, false, true, false, None);
24+
let overrides = build_dll_overrides(true, false, false, true, false, None, false);
2525

2626
// DXVK keys should be present
2727
assert!(overrides.contains("d3d9=n,b"));
@@ -34,7 +34,7 @@ fn test_build_dll_overrides_dxvk_active() {
3434

3535
#[test]
3636
fn test_build_dll_overrides_vkd3d_active() {
37-
let overrides = build_dll_overrides(false, true, false, true, false, None);
37+
let overrides = build_dll_overrides(false, true, false, true, false, None, false);
3838

3939
// VKD3D keys should be present
4040
assert!(overrides.contains("d3d12=n,b"));
@@ -49,10 +49,39 @@ fn test_build_dll_overrides_local_dll_skip() {
4949
let d3d11_path = tmp.path().join("d3d11.dll");
5050
std::fs::write(&d3d11_path, "fake dll").unwrap();
5151

52-
let overrides = build_dll_overrides(true, false, false, true, false, Some(tmp.path()));
52+
let overrides = build_dll_overrides(true, false, false, true, false, Some(tmp.path()), false);
5353

5454
// d3d11 should be skipped because it exists locally
5555
assert!(!overrides.contains("d3d11=n,b"));
5656
// other dxvk keys should still be present
5757
assert!(overrides.contains("d3d9=n,b"));
5858
}
59+
60+
#[test]
61+
fn test_build_dll_overrides_strict_dxvk() {
62+
let overrides = build_dll_overrides(true, false, false, true, false, None, true);
63+
64+
// DXVK keys should use 'n' (native only) in strict mode
65+
assert!(overrides.contains("d3d9=n"));
66+
assert!(overrides.contains("d3d11=n"));
67+
assert!(overrides.contains("dxgi=n"));
68+
assert!(overrides.contains("d3d8=n"));
69+
assert!(overrides.contains("d3d10core=n"));
70+
71+
// They should NOT contain 'n,b'
72+
assert!(!overrides.contains("d3d9=n,b"));
73+
assert!(!overrides.contains("d3d11=n,b"));
74+
}
75+
76+
#[test]
77+
fn test_build_dll_overrides_strict_dxvk_ignores_local() {
78+
let tmp = tempfile::tempdir().unwrap();
79+
let d3d11_path = tmp.path().join("d3d11.dll");
80+
std::fs::write(&d3d11_path, "fake dll").unwrap();
81+
82+
let overrides = build_dll_overrides(true, false, false, true, false, Some(tmp.path()), true);
83+
84+
// In strict mode, even if d3d11.dll exists locally, we should still add the override
85+
// and it should be 'n' (native only)
86+
assert!(overrides.contains("d3d11=n"));
87+
}

0 commit comments

Comments
 (0)