fix: map GraphicsProfile.Level_11_2 to a valid Direct3D feature level - #3302
fix: map GraphicsProfile.Level_11_2 to a valid Direct3D feature level#3302sasvdw wants to merge 3 commits into
Conversation
GraphicsProfile.Level_11_2 (0xB200) has no corresponding native Direct3D feature level: real feature levels jump from 11_1 (0xB100) to 12_0 (0xC000) (Direct3D 11.2 was an API revision that runs on FL 11_1 hardware, not a new feature level). ToFeatureLevel raw-cast the profile straight to 0xB200, so selecting Level_11_2 failed on every machine: Direct3D11 rejected it in GraphicsAdapter.IsProfileSupported (the exact-match check can never match a non-existent level), and Direct3D12 passed its unconditional IsProfileSupported check but then failed at CreateDevice(0xB200). Vulkan ignored the profile and ran, which is the tell that the fault was in the Direct3D mapping, not the enum. Map Level_11_2 to its real capability tier (FL 11_1) in ToFeatureLevel, drive the Direct3D11 IsProfileSupported check through ToFeatureLevel (instead of a raw cast), and list Level_11_2 alongside 11_0/11_1 in the shader-model 5_0 case so the compiler no longer throws for it. Direct3D11 and Direct3D12 now accept Level_11_2 and run at FL 11_1, matching Vulkan. Legitimate rejection is preserved: profiles a device genuinely cannot provide (e.g. Level_11_x on FL10-only hardware) still fail the capability check as before. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a regression test for the Direct3D GraphicsProfile -> D3DFeatureLevel mapping: Level_11_2 must resolve to the real FL 11_1 (not the non-existent 0xB200), and every profile must map to a defined D3DFeatureLevel. Verified red/green against the ToFeatureLevel fix (both facts fail on the raw cast, pass on the fix). Reference Silk.NET.Direct3D11 in the test project so D3DFeatureLevel is visible at compile time (Stride.Graphics hides it via PrivateAssets); the test is guarded by STRIDE_GRAPHICS_API_DIRECT3D since Stride.Graphics.Tests builds per graphics API. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
🤖 Draft PR — automatic CI is skipped to save runner minutes.
|
|
LGTM. Good catch! A little nitpick: The comments look a bit too verbose for my taste (even though I tend to write big explanatory comments 😁) |
|
@Ethereal77, happy to take suggestions... Was a really tough one from a comments point of view. There's a genuine footgun highlighted in the comments that is bound to cause issues if someone just looks at the changes at surface level. |
|
I agree with you. Better documentation is always preferable, specially around conflicting code. But, I think that if you have already created and used a helper In the shader compiler, as it does not use the helper, the comment makes sense. |
Address review feedback on the verbose comments by removing the reason they were needed. The helper relied on GraphicsProfile values happening to equal D3D feature levels, so every conversion was a cast and the Level_11_2 special case had to be explained in prose at each site. An explicit mapping table documents itself: Level_11_1 and Level_11_2 sharing a row states the fix outright, so the call-site comment in GraphicsAdapter and the shader model comment in ShaderCompiler are both dropped, and the helper's own explanation shrinks to a one-line remark. Also removes the unused array overload, whose span reinterpret bypassed the mapping entirely and would still have produced the non-existent 0xB200, and extends the tests to pin every profile so a mis-mapping cannot pass. FromFeatureLevel is left as-is; it is a separate direction with its own unmapped-value question and no bearing on this fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@Ethereal77, after further consideration, I think I've landed on something more concise while also adding tests to ensure everything stays up to date. I also removed the comment from the shader compiler because it's an entirely different case and should be straightforward knowledge for anyone with an understanding of this domain. |
|
👍 Looks nice |
PR Details
Summary —
GraphicsProfile.Level_11_2is unusable on the Direct3D backends; this maps it to thereal feature level it represents (FL 11_1) so it behaves like every other profile.
Description —
GraphicsProfile.Level_11_2 = 0xB200has no corresponding native Direct3D featurelevel (real levels jump
11_1 (0xB100) → 12_0 (0xC000); Direct3D 11.2 was an API revision that runson FL 11_1 hardware, not a new feature level).
GraphicsProfileHelper.ToFeatureLevelraw-cast theprofile to the non-existent
0xB200, so selecting Level 11.2 crashed at startup on Direct3D11(rejected in
GraphicsAdapter.IsProfileSupported) and Direct3D12 (passed its unconditionalIsProfileSupported, then failed atCreateDevice(0xB200)), while Vulkan — which ignores theprofile — ran.
Three Direct3D code paths change; the
GraphicsProfileenum is untouched, so this is non-breakingwith no serialization impact:
GraphicsProfileHelper.ToFeatureLevelmapsLevel_11_2 → 11_1instead of emitting0xB200.Every other profile equals a real
D3DFeatureLeveland keeps the direct cast.GraphicsAdapter.Direct3D.cs(Direct3D11IsProfileSupported) resolves the feature level viaToFeatureLevel()rather than a raw cast, so the exact-match check tests the real FL 11_1.ShaderCompiler.cs(ShaderProfileFromGraphicsProfile) listsLevel_11_2alongside11_0/11_1in the shader-model5_0case so it no longer throws.Direct3D11 and Direct3D12 now accept
Level_11_2and run at FL 11_1, matching Vulkan. Legitimaterejection is preserved: profiles a device genuinely cannot provide (e.g.
Level_11_xon FL10-onlyhardware) still fail the capability check as before — only the invalid-value failure is removed.
Motivation and context — see #3301. The failure is hardware-independent: a WARP
software-renderer proof (in the repro) shows WARP reporting FL 12_1 yet rejecting
0xB200withE_INVALIDARGwhile accepting0xB100. The mapping has been wrong since the 2018 open-source commit;a June-2025 documentation pass added the
[Display("Level 11.2 ~ …")]tooltip that makes the unusableoption look legitimate in Game Settings.
Related Issue
Fixes #3301. Repro (minimal code-only game + WARP proof):
https://github.com/sasvdw/stride-graphicsprofile-level112-repro
Types of changes
Checklist
Validation status (kept honest — check the boxes above as each is satisfied):
becomes accurate.
sources/engine/Stride.Graphics.Tests/TestGraphicsProfileHelper.cscovers the mapping —Level_11_2resolves to FL 11_1 (not0xB200), and every profile maps to a definedD3DFeatureLevel. Verified red/green (both facts fail on the raw cast, pass on the fix).Stride.Graphics(Direct3D11 + Direct3D12) andStride.Shaders.Compilersbuild clean;the full test suite runs in CI.
launches (this is the mandatory personal-testing step before marking the PR ready).