Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved: D3D11 Adapter Creation Speed by Manually Selecting Hardware Type #16035

Merged
merged 3 commits into from
Jun 17, 2024

Conversation

Sewer56
Copy link
Contributor

@Sewer56 Sewer56 commented Jun 15, 2024

What does the pull request do?

This pull request speeds up the creation of the D3D11 device during startup by manually selecting the hardware type.

This results in a significant improvement in startup time when using Win D3D11 on NAOT.

What is the current behavior?

Currently, the CreateD3D11Display method in AngleWin32EglDisplay.cs uses D3D_DRIVER_TYPE_UNKNOWN when creating the D3D11 device.

This can lead to slower startup times as the runtime needs to determine the appropriate driver type based on the available adapters.

What is the updated/expected behavior with this PR?

With this PR, the startup time of Avalonia applications using Direct3D 11 is improved by approximately 40-50ms on most machines. On my desktop this results in a reduction of 75ms -> 25ms to initialize Avalonia under AOT.

(Measured from AppBuilder.Configure to running Initialize in App.axaml.cs)

The CreateD3D11Display method now explicitly selects the appropriate driver type (D3D_DRIVER_TYPE_HARDWARE or D3D_DRIVER_TYPE_WARP) based on the characteristics of the chosen adapter. (With a fallback to legacy D3D_DRIVER_TYPE_SOFTWARE)

This eliminates the overhead of the runtime determining the driver type and makes the creation of the D3D11 device essentially free. Since we're already querying the available display adapters, it would be a waste to not use that information.

How was the solution implemented?

  • Added a check to determine if the chosen adapter is a software adapter by examining the Flags property of the DXGI_ADAPTER_DESC1 structure.
  • If the adapter is a software adapter, D3D_DRIVER_TYPE_WARP is used as the driver type. Otherwise, D3D_DRIVER_TYPE_HARDWARE is used.
  • If the initial attempt to create the D3D11 device fails (adapter is Software and WARP is not supported, very very rare), a fallback mechanism tries to create the device with legacy D3D_DRIVER_TYPE_SOFTWARE driver type.

And also

  • Introduced ThrowFormattedException helper methods to allow callers to be inlined.

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0049058-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@kekekeks
Copy link
Member

I suspect that it will break virtual machines with Microsoft Basic Render Device

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0049076-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@Sewer56
Copy link
Contributor Author

Sewer56 commented Jun 16, 2024

I suspect that it will break virtual machines with Microsoft Basic Render Device

@kekekeks

image

image

It was my first time trying QEMU+KVM for Windows, but I've tried both the RedHat driver and the Basic Microsoft Driver with no issues.

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0049079-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@maxkatz6 maxkatz6 added this pull request to the merge queue Jun 17, 2024
Merged via the queue into AvaloniaUI:master with commit af104b9 Jun 17, 2024
10 checks passed
@maxkatz6
Copy link
Member

@Sewer56 looks like this PR break Angle/DX on my machine:

[OpenGL]Unable to initialize ANGLE-based rendering with DirectX11 : 'System.ArgumentException: Value does not fall within the expected range.
   at Avalonia.Win32.DirectX.DirectXUnmanagedMethods.D3D11CreateDevice(IntPtr adapter, D3D_DRIVER_TYPE DriverType, IntPtr Software, UInt32 Flags, D3D_FEATURE_LEVEL[] pFeatureLevels, UInt32 FeatureLevels, UInt32 SDKVersion, IntPtr& ppDevice, D3D_FEATURE_LEVEL& pFeatureLevel, IntPtr* ppImmediateContext)
   at Avalonia.Win32.OpenGl.Angle.AngleWin32EglDisplay.CreateD3D11Device(IDXGIAdapter1 chosenAdapter, D3D_FEATURE_LEVEL[] featureLevels) in E:\Work\Projects\AvaloniaCopy1\src\Windows\Avalonia.Win32\OpenGl\Angle\AngleWin32EglDisplay.cs:line 175
   at Avalonia.Win32.OpenGl.Angle.AngleWin32EglDisplay.CreateD3D11Display(Win32AngleEglInterface egl) in E:\Work\Projects\AvaloniaCopy1\src\Windows\Avalonia.Win32\OpenGl\Angle\AngleWin32EglDisplay.cs:line 106
   at Avalonia.Win32.OpenGl.Angle.D3D11AngleWin32PlatformGraphics.TryCreate(Win32AngleEglInterface egl) in E:\Work\Projects\AvaloniaCopy1\src\Windows\Avalonia.Win32\OpenGl\Angle\D3D11AngleWin32PlatformGraphics.cs:line 77'
[OpenGL]Unknown requested PlatformApi 'DirectX11'

There are couple of problems:

  1. D3D11CreateDevice+HARDWARE fails here with invalid argument. Don't really know why.
  2. D3D11CreateDevice+SOFTWARE fails too, but UNKNOWN works fine.
  3. D3D11CreateDevice return type should be "int" (non-void), and PreserveSig = false should be removed, if you want to implement fallback logic.
  4. Before each fallback, there should be a warning log message, so developers can easier find this problem. Something like:
Logger.TryGet(LogEventLevel.Warning, LogArea.Win32Platform)?.Log(null, "Unable to create hardware ID3D11Device, error code = {ErrorCode}", $"0x{result:X}")

@Sewer56
Copy link
Contributor Author

Sewer56 commented Jun 19, 2024

D3D11CreateDevice return type should be "int" (non-void),

I think the core issue lies here.

Technically speaking, you're supposed to check the HRESULT you obtain from D3D11CreateDevice to determine the successful creation of a device. A non-zero HRESULT indicating an error. For this, as you mentioned we should remove PreserveSig.

The existing Avalonia code before this PR however did not do this. It checked the ppDevice parameter to see if a value was written to it instead.

In order to keep the amount of changes from main, minimal I left that unchanged and used the existing strategy Avalonia was using in place; even though it's not the idiomatic approach.

I did actually originally change the signature to fix this up (you can see it in earlier commits), but cleaned up to reduce the diff size from main.

Edit: I didn't know PreserveSig actually caused exceptions to be thrown on bad HRESULT, as I've never used the feature, so I didn't catch that. And it seems like reviewer didn't catch it either.


@maxkatz6 Should I throw a patch for this?

With regards of what needs to be done, I think you're right on the money. Remove PreserveSig, and check on HRESULT like we really should have been doing in the first place.

The patch for this should be trivial, but you're the one who will have to test either way since the hardware's on your end. I wouldn't mind throwing the PR in for this though.

@Sewer56
Copy link
Contributor Author

Sewer56 commented Jun 19, 2024

Ah, I was about to throw a PR against main to fix either way.
But since it got reverted, I cherry picked and threw it into a commit at Sewer56@ec04ffa (Edit: and Sewer56@2873249 , Sewer56@42abfdc )

In any case, what would be the preferred course of action here? Should I re-submit?

@kekekeks
Copy link
Member

Yes, a new PR with updated version of the change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants