diff --git a/sources/engine/Stride.Graphics.Tests/Stride.Graphics.Tests.csproj b/sources/engine/Stride.Graphics.Tests/Stride.Graphics.Tests.csproj
index 69fdf60fad..a9220ab5ec 100644
--- a/sources/engine/Stride.Graphics.Tests/Stride.Graphics.Tests.csproj
+++ b/sources/engine/Stride.Graphics.Tests/Stride.Graphics.Tests.csproj
@@ -14,6 +14,9 @@
+
+
+
diff --git a/sources/engine/Stride.Graphics.Tests/TestGraphicsProfileHelper.cs b/sources/engine/Stride.Graphics.Tests/TestGraphicsProfileHelper.cs
new file mode 100644
index 0000000000..65f793c4fc
--- /dev/null
+++ b/sources/engine/Stride.Graphics.Tests/TestGraphicsProfileHelper.cs
@@ -0,0 +1,53 @@
+// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net)
+// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
+
+#if STRIDE_GRAPHICS_API_DIRECT3D
+
+using System;
+
+using Xunit;
+
+using Silk.NET.Core.Native;
+
+namespace Stride.Graphics.Tests;
+
+///
+/// Regression tests for the GraphicsProfile -> D3DFeatureLevel mapping in GraphicsProfileHelper.
+/// Direct3D-only: the helper and D3DFeatureLevel exist only under STRIDE_GRAPHICS_API_DIRECT3D.
+///
+public class TestGraphicsProfileHelper
+{
+ [Theory]
+ [InlineData(GraphicsProfile.Level_9_1, D3DFeatureLevel.Level91)]
+ [InlineData(GraphicsProfile.Level_9_2, D3DFeatureLevel.Level92)]
+ [InlineData(GraphicsProfile.Level_9_3, D3DFeatureLevel.Level93)]
+ [InlineData(GraphicsProfile.Level_10_0, D3DFeatureLevel.Level100)]
+ [InlineData(GraphicsProfile.Level_10_1, D3DFeatureLevel.Level101)]
+ [InlineData(GraphicsProfile.Level_11_0, D3DFeatureLevel.Level110)]
+ [InlineData(GraphicsProfile.Level_11_1, D3DFeatureLevel.Level111)]
+ [InlineData(GraphicsProfile.Level_11_2, D3DFeatureLevel.Level111)]
+ public void Profile_MapsToExpectedFeatureLevel(GraphicsProfile profile, D3DFeatureLevel expected)
+ {
+ Assert.Equal(expected, profile.ToFeatureLevel());
+ }
+
+ [Fact]
+ public void EveryProfile_MapsToADefinedFeatureLevel()
+ {
+ // Guards against a profile added later without a matching entry in the mapping.
+ foreach (GraphicsProfile profile in Enum.GetValues())
+ {
+ var featureLevel = profile.ToFeatureLevel();
+ Assert.True(Enum.IsDefined(featureLevel),
+ $"{profile} (0x{(int)profile:X4}) maps to undefined D3DFeatureLevel 0x{(int)featureLevel:X4}");
+ }
+ }
+
+ [Fact]
+ public void UnknownProfile_Throws()
+ {
+ Assert.Throws(() => ((GraphicsProfile)0x1234).ToFeatureLevel());
+ }
+}
+
+#endif
diff --git a/sources/engine/Stride.Graphics/Direct3D/GraphicsAdapter.Direct3D.cs b/sources/engine/Stride.Graphics/Direct3D/GraphicsAdapter.Direct3D.cs
index 4efc67bc35..b6f376cd41 100644
--- a/sources/engine/Stride.Graphics/Direct3D/GraphicsAdapter.Direct3D.cs
+++ b/sources/engine/Stride.Graphics/Direct3D/GraphicsAdapter.Direct3D.cs
@@ -269,7 +269,7 @@ public bool IsProfileSupported(GraphicsProfile graphicsProfile)
ID3D11DeviceContext* deviceContext = null;
D3DFeatureLevel matchedFeatureLevel = 0;
- var featureLevel = (D3DFeatureLevel) graphicsProfile;
+ var featureLevel = graphicsProfile.ToFeatureLevel();
var featureLevels = stackalloc D3DFeatureLevel[] { featureLevel };
HResult result = d3d11.CreateDevice(pAdapter: null, D3DDriverType.Hardware, Software: IntPtr.Zero,
diff --git a/sources/engine/Stride.Graphics/Direct3D/GraphicsProfileHelper.cs b/sources/engine/Stride.Graphics/Direct3D/GraphicsProfileHelper.cs
index 10381abd93..05d83eee95 100644
--- a/sources/engine/Stride.Graphics/Direct3D/GraphicsProfileHelper.cs
+++ b/sources/engine/Stride.Graphics/Direct3D/GraphicsProfileHelper.cs
@@ -3,9 +3,9 @@
#if STRIDE_GRAPHICS_API_DIRECT3D
-using Silk.NET.Core.Native;
+using System;
-using Stride.Core.UnsafeExtensions;
+using Silk.NET.Core.Native;
namespace Stride.Graphics;
@@ -14,26 +14,28 @@ namespace Stride.Graphics;
///
internal static class GraphicsProfileHelper
{
- ///
- /// Converts an array of s to an array of corresponding s.
- ///
- /// An array of s to convert.
- /// An array of Direct3D s.
- public static D3DFeatureLevel[] ToFeatureLevel(this GraphicsProfile[] profiles)
- {
- if (profiles is null or [])
- return null;
-
- var featureLevels = profiles.AsReadOnlySpan().ToArray();
- return featureLevels;
- }
-
///
/// Converts a to its corresponding .
///
/// A to convert.
/// A Direct3D .
- public static D3DFeatureLevel ToFeatureLevel(this GraphicsProfile profile) => (D3DFeatureLevel) profile;
+ /// is not a known profile.
+ ///
+ /// Direct3D has no feature level 11.2, as 11.2 was an API revision running on 11_1 hardware, so
+ /// maps to 11_1, its real capability tier.
+ ///
+ public static D3DFeatureLevel ToFeatureLevel(this GraphicsProfile profile) => profile switch
+ {
+ GraphicsProfile.Level_9_1 => D3DFeatureLevel.Level91,
+ GraphicsProfile.Level_9_2 => D3DFeatureLevel.Level92,
+ GraphicsProfile.Level_9_3 => D3DFeatureLevel.Level93,
+ GraphicsProfile.Level_10_0 => D3DFeatureLevel.Level100,
+ GraphicsProfile.Level_10_1 => D3DFeatureLevel.Level101,
+ GraphicsProfile.Level_11_0 => D3DFeatureLevel.Level110,
+ GraphicsProfile.Level_11_1 or GraphicsProfile.Level_11_2 => D3DFeatureLevel.Level111,
+
+ _ => throw new ArgumentOutOfRangeException(nameof(profile), profile, "Unknown graphics profile.")
+ };
///
/// Converts a to its corresponding .
diff --git a/sources/shaders/Stride.Shaders.Compilers/Direct3D/ShaderCompiler.cs b/sources/shaders/Stride.Shaders.Compilers/Direct3D/ShaderCompiler.cs
index 7676823ac6..013fcbaa05 100644
--- a/sources/shaders/Stride.Shaders.Compilers/Direct3D/ShaderCompiler.cs
+++ b/sources/shaders/Stride.Shaders.Compilers/Direct3D/ShaderCompiler.cs
@@ -161,7 +161,7 @@ static string ShaderProfileFromGraphicsProfile(GraphicsProfile graphicsProfile)
GraphicsProfile.Level_9_3 => "4_0_level_9_3",
GraphicsProfile.Level_10_0 => "4_0",
GraphicsProfile.Level_10_1 => "4_1",
- GraphicsProfile.Level_11_0 or GraphicsProfile.Level_11_1 => "5_0",
+ GraphicsProfile.Level_11_0 or GraphicsProfile.Level_11_1 or GraphicsProfile.Level_11_2 => "5_0",
_ => throw new ArgumentException("Graphics Profile not supported.", nameof(graphicsProfile))
};