@@ -9,28 +9,86 @@ impl LaunchValidator for LaunchInvariantValidator {
99 fn validate ( & self , ctx : & mut PipelineContext ) {
1010 let mut warnings = Vec :: new ( ) ;
1111
12- // 1. Invariant B: baseline/WineD3D must not keep forced native DXVK/VKD3D overrides
13- if ctx. graphics_stack . effective_backend == "WineD3D (Baseline)" {
12+ // Invariant A: If effective GPU is unset/default, there must be no forced GPU-selection env vars.
13+ if ctx. graphics_stack . effective_gpu . is_none ( ) {
14+ if let Some ( spec) = & ctx. command_spec {
15+ for key in & [
16+ "DRI_PRIME" ,
17+ "__NV_PRIME_RENDER_OFFLOAD" ,
18+ "__NV_PRIME_RENDER_OFFLOAD_PROVIDER" ,
19+ "__GLX_VENDOR_LIBRARY_NAME" ,
20+ "__VK_LAYER_NV_optimus" ,
21+ ] {
22+ if spec. env . contains_key ( * key) {
23+ warnings. push ( (
24+ "INVARIANT_A_VIOLATION" ,
25+ format ! ( "Effective GPU is unset but found GPU-forcing env var: {}" , key) ,
26+ ) ) ;
27+ }
28+ }
29+ }
30+ }
31+
32+ // Invariant B: If effective backend is not DXVK, there must be no DXVK-forcing overrides or DXVK-only DLL path injection.
33+ if ctx. graphics_stack . effective_backend != "DXVK" {
1434 if let Some ( spec) = & ctx. command_spec {
1535 if let Some ( overrides) = spec. env . get ( "WINEDLLOVERRIDES" ) {
16- let conflicts = [ "d3d8" , "d3d9" , "d3d10" , "d3d10_1" , " d3d10core", "d3d11" , "dxgi" , "d3d12" , "d3d12core "] ;
36+ let dxvk_dlls = [ "d3d8" , "d3d9" , "d3d10" , "d3d10core" , "d3d11" , "dxgi" ] ;
1737 for part in overrides. split ( ';' ) {
1838 if let Some ( ( dll, mode) ) = part. split_once ( '=' ) {
1939 let dll_trimmed = dll. trim ( ) . to_lowercase ( ) ;
20- if conflicts . contains ( & dll_trimmed. as_str ( ) ) && mode. contains ( 'n' ) {
40+ if dxvk_dlls . contains ( & dll_trimmed. as_str ( ) ) && mode. contains ( 'n' ) {
2141 warnings. push ( (
22- "INVARIANT_B_CONFLICT " ,
23- format ! ( "Forced native override '{dll}={mode}' found in baseline mode. Graphics may fail." )
42+ "INVARIANT_B_VIOLATION " ,
43+ format ! ( "Effective backend is not DXVK but found native override for DXVK DLL: {}" , dll ) ,
2444 ) ) ;
2545 }
2646 }
2747 }
2848 }
49+ if let Some ( dll_path) = spec. env . get ( "WINEDLLPATH" ) {
50+ if dll_path. contains ( "dxvk" ) {
51+ warnings. push ( (
52+ "INVARIANT_B_VIOLATION" ,
53+ "Effective backend is not DXVK but WINEDLLPATH contains 'dxvk'" . into ( ) ,
54+ ) ) ;
55+ }
56+ }
2957 }
3058 }
3159
32- // 2. Invariant C: effective D3D12 provider must match resolved provider paths
33- if !ctx. graphics_stack . effective_d3d12_provider . is_empty ( ) {
60+ // Invariant C: If effective D3D12 provider is unset/default/not-selected, there must be no forced D3D12 provider injection.
61+ if ctx. graphics_stack . effective_d3d12_provider == "None" {
62+ if let Some ( spec) = & ctx. command_spec {
63+ if let Some ( overrides) = spec. env . get ( "WINEDLLOVERRIDES" ) {
64+ let d3d12_dlls = [ "d3d12" , "d3d12core" ] ;
65+ for part in overrides. split ( ';' ) {
66+ if let Some ( ( dll, mode) ) = part. split_once ( '=' ) {
67+ let dll_trimmed = dll. trim ( ) . to_lowercase ( ) ;
68+ if d3d12_dlls. contains ( & dll_trimmed. as_str ( ) ) && mode. contains ( 'n' ) {
69+ warnings. push ( (
70+ "INVARIANT_C_VIOLATION" ,
71+ format ! ( "Effective D3D12 provider is None but found native override for DLL: {}" , dll) ,
72+ ) ) ;
73+ }
74+ }
75+ }
76+ }
77+ if let Some ( dll_path) = spec. env . get ( "WINEDLLPATH" ) {
78+ if dll_path. contains ( "vkd3d" ) {
79+ warnings. push ( (
80+ "INVARIANT_C_VIOLATION" ,
81+ "Effective D3D12 provider is None but WINEDLLPATH contains 'vkd3d'" . into ( ) ,
82+ ) ) ;
83+ }
84+ }
85+ }
86+ }
87+
88+ // Detailed Invariant C: Effective D3D12 provider must match resolved provider paths if one is active.
89+ // If effective is "vkd3d-proton", we expect to see it in the path.
90+ // If effective is "vkd3d", we expect to see vkd3d but NOT vkd3d-proton in the path.
91+ if ctx. graphics_stack . effective_d3d12_provider != "None" {
3492 let provider = & ctx. graphics_stack . effective_d3d12_provider ;
3593 for res in & ctx. dll_resolutions {
3694 if res. name == "d3d12" || res. name == "d3d12core" {
@@ -52,9 +110,15 @@ impl LaunchValidator for LaunchInvariantValidator {
52110 }
53111 }
54112
55- // 3. Invariant D: explicit user setting must not be silently overwritten
113+ // Invariant D: explicit user setting must not be silently overwritten
56114 if !ctx. graphics_stack . requested_backend . is_empty ( ) && ctx. graphics_stack . requested_backend != "Auto" {
57- if ctx. graphics_stack . requested_backend != ctx. graphics_stack . effective_backend {
115+ // requested_backend is likely Debug string of enum, e.g. "DXVK" or "WineD3D"
116+ let req = & ctx. graphics_stack . requested_backend ;
117+ let eff = & ctx. graphics_stack . effective_backend ;
118+
119+ let mismatch = ( req == "DXVK" && eff != "DXVK" ) || ( req == "WineD3D" && eff != "WineD3D (Baseline)" ) ;
120+
121+ if mismatch {
58122 let reason = ctx. graphics_stack . fallback_reasons . get ( "graphics_backend" ) . cloned ( ) . unwrap_or_else ( || "unknown" . into ( ) ) ;
59123 warnings. push ( (
60124 "INVARIANT_D_BACKEND_MISMATCH" ,
@@ -76,7 +140,15 @@ impl LaunchValidator for LaunchInvariantValidator {
76140 }
77141
78142 if let Some ( requested_gpu) = & ctx. graphics_stack . requested_gpu {
79- if ctx. graphics_stack . effective_gpu . as_ref ( ) != Some ( requested_gpu) {
143+ let mut mismatch = true ;
144+ if let Some ( effective_gpu) = & ctx. graphics_stack . effective_gpu {
145+ // Check for partial match since effective names are synthesized
146+ if effective_gpu. contains ( requested_gpu) || requested_gpu. contains ( "NVIDIA" ) && effective_gpu. contains ( "NVIDIA" ) {
147+ mismatch = false ;
148+ }
149+ }
150+
151+ if mismatch {
80152 let reason = ctx. graphics_stack . fallback_reasons . get ( "gpu" ) . cloned ( ) . unwrap_or_else ( || "unknown" . into ( ) ) ;
81153 warnings. push ( (
82154 "INVARIANT_D_GPU_MISMATCH" ,
0 commit comments