DirectX in NativeAOT

Andrii Kurdiumov
ITNEXT
Published in
3 min readJun 21, 2021

--

I’m always looking for a diverse set of fun projects where I can show the strengths of Native AOT. So today I will write about DirectX. Technically, DirectX is just another COM library, and because I wrote about COM in NativeAOT earlier, it’s nothing special in that regards. What’s new in this article is that today I will use source generators to quickly create a ComWrappers instance ready to use within my small application.

As a starting point, I will again take Winterop library from Jeremy Kuhne. If at any point you become bored, go directly to source code and play with the sample.

So what’s currently needed to make NativeAOT and COM spin? Usually, I start porting the application to NativeAOT with the following simple steps. I create a new blank ComWrappers class.

UniversalWrapper here, is a class that will serve as generic runtime callable wrapper for all COM objects that I will use in the application.

Then I register ComWrappers for global marshalling, so all COM interop will be forwarded to newly created ComWrappers.

Then I run the application until it throws somewhere. In the current case, it was during the conversion of my universal wrapper interface to Winterop.Direct2d.IFactory.

This means that I should implement Winterop.Direct2d.IFactory on RCW to make that part of the code works. That’s done by slapping attribute on the UnversalWrappers class. All implementation required for this interface would be done by WinFormsComInterop.SourceGenerator NuGet package.

I run build again and it fails with 2 errors

error CS8121: An expression of type ‘IGeometry’ cannot be handled by a pattern of type ‘UniversalWrapper’
error CS8121: An expression of type ‘IRenderingParams’ cannot be handled by a pattern of type ‘UniversalWrapper’.

I add two more required interfaces.

Then I build again, it fails, add required interfaces. Rinse and repeat. Rinse and repeat. Finally this process ends, and build succeed.

Now I run the application again. Guess what?

Crash again. So I add one more interface using attribute[RuntimeCallableWrapper(typeof(WInterop.DirectWrite.IFactory))]. After some time, it settles, we implement all the interfaces, and run the application again.

Last screen in example

That’s it. Now the application is ready to be tried in the Native AOT context. Let’s run dotnet publish -c Release -r win-x64 and see how it going. Hey! it’s working. It’s again 1,4 Mb of disk space.

P.S. I should note, that I blatantly abuse ComWrappers here, and for best effect, you should follow the path of CsWinRT and use the slimmer version of ComWrappers, and structure code generation in a slightly different manner, which do not require having a global ComWrapper instance. My library and source generator featured in this article serves different purposes — make older application working in Native AOT context. The second goal is to lessen the pain to run them, if somebody would like to try that.

P.S.S. I would call to arms again! Join the force! Help the project by running your application using Native AOT, in case of issues go and chat at Gitter, file issues at Github repo, people are super helpful here.

--

--