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)) };