Blog post: Porting Unity to CoreCLR


My team and I are still hard at work bringing the latest .NET technology to Unity users. As one person leading this effort, I’m excited to share further progress with you. Part of our work involves making existing Unity code work with the .NET CoreCLR JIT runtime, including a highly performant, more advanced, and more efficient garbage collector (GC).

A new blog post – published today – covers recent changes we’ve made to allow the CoreCLR GC to work hand in hand with Unity engine native code. Read it here, then please use this thread for discussion of the blog or to ask me questions.

30 Likes

Wish you all the best!!!
Just one question 2024 or later? :smile:

Sorry, we’re not ready to announce publicly when this will ship.

I am a big fan of these kinds of blog posts. That leaves me torn, though, between wanting to see more posts and wanting the focus to stay on the actual task. Either way. Keep at it, can’t wait to see the end result.

I’m a manager supporting the some of the teams involved in this work - I’m not actually committing code directly for it, most of the time. So the time taken for me to write the blog post is not really taking away focus from the work here, no worries!

1 Like

Amazing blogpost, code landia really crystalized the concept! 2 questions come to mind:

  • Have you already determined every place the code would need to be updated or are you still finding sources of native memory “leaks” as you are progressing? This ties into my next question:

  • May I ask how much code is left? 25%, 50%, 75%? Of course, not all code is created equal and it is understandable that being 80% done doesn’t mean the remaining 20% will take 1/4th the time (like a progress bar that hangs). Still, it would be great to get this insight.

I have some question about GCHandle performance. In one of my project I’ve used C++ as “scripting” language and I was using GCHandle along with shared_ptr to reference unity managed instances. Back then I was testing (and also reading lot of stuff around interop) and found that at least if you don’t use pinned GCHandle, then the performance was good (In my case, using it also with custom non-mt shared_ptr, the only bottleneck is alloc and dealloc; I wasn’t allocating a lot of managed objects per frame, to be honest). So I’m interesting in what situation you encountered the GCHandle performance problems?

I think that we have found everything now. Since some of the causes are only detectable at run time, I’m sure their are some we have missed, but we’ve found thousands of places where were know native code is accessing managed memory in ways we want to avoid.

I think we have addressed about 95% of the code we know about. Of course that last 5% has some difficult cases, as you suggest. Although we did try to prioritize working on the most difficult cases at the start, to prove that we actually could do this, so many of those were handled about a year ago.

I want to be careful to not imply that we are 95% done with all of the work to ship the CoreCLR runtime with Unity, by the way. GC safety is one aspect of this work, but there are others.

11 Likes

The performance issues we have seen with GCHandle objects revolves mostly around creation and freeing those objects. The CoreCLR runtime is really quick to access the managed object from a GCHandle (the cost is the same as a pointer deference in C), so there is really no overhead once the GCHandle object is created.

Techniques like GCHandle pooling can help performance in places where you must create and free them often, but we’re generally working to avoid that as much as possible.

You are current that pinned GCHandle objects have a larger performance hit, because they place more constraints on the GC.

4 Likes

Still, 95% must feel pretty good to say out loud :smile:

Good luck with the remaining 5% to you and the rest of the team!

3 Likes

Nice write up thanks Josh. When you do make the more to CoreCLR will it generally be straight to .NET 8+ and all of its libs or are there yet more large stepping stones under the water to navigate?

1 Like

Nice! Love the blogpost. I had a bit of a clarifying question with AssemblyLoadContext in the editor. As I understand it, all C# code, including say HDRP and UI toolkit for example (Which is used all across the editor at this point) needs to be reinitialized on each domain reload. If with AssemblyLoadContext, they don’t need to be reloaded, they don’t have to get reinitialized each time, right? As it stands there is an aditional 2-3 second wait after each domain reload for tasks like repainting the inspector, would that theoretically go away with CoreCLR?

Great set of info. That was an insightful blog entry to read!
Makes one excited for the future of Unity (even if I haven’t run into GC related problems that much myself).

2 Likes

Yes, we’re looking to support .NET 8 now, including all of the base class libraries. There are many libraries out there in the .NET ecosystem, too many to test in fact, so we cannot guarantee support for all of them. But our goal is to make Unity more and more like a “normal” .NET application so that if can interact well with the rest of the .NET ecosystem.

9 Likes

Yes! I don’t have exact numbers yet, as we are still working on the implementation. But the idea is to have multiple assembly load contexts, to allow the Unity editor to only unload and reinitialize the code that actually changes. Some of our early tests here should tremendous promise to lower the cost of iteration time in the editor due to code changes. This is one of our primary goals of this entire development effort!

19 Likes

Does that include managed threads in WebGL?

That is part of the goal, yes. But it is also orthogonal to some of the other work to ship CoreCLR work, and is proceeding separately. So we may ship managed threads in WebGL without CoreCLR support, or CoreCLR support without managed threads in WebGL, depending on the timing of both.

Maybe I’m dumb but isn’t that the same thing?

There is a good bit of complexity for the “WebGL” platform related to managed threads and the garbage collector. The way Unity currently uses the Boehm GC requires the platform allow all threads to be paused and all call stacks to be walked via some kind of API. WebGL does not provide these APIs, so Unity has historically work around that restriction by limiting managed code to the main thread for WebGL. Then the engine can be sure of times when it is safe to allow the GC to run (e.g. before each frame, as there is no managed code executing).

Many of the change described in this blog post allow the engine code to be more cooperative with the GC, meaning we can start to lift some of the platform restrictions which make multi-threaded managed code on WebGL difficult. So the CoreCLR GC work is a bit of a prerequisite for multi-threaded managed code on WebGL.

But at the same time, WebGL uses IL2CPP only, and since that is entirely Unity’s .NET runtime, we have the ability to modify it some to work better with WebGL, even without the engine code being fully cooperative with the GC.

So there are multiple ways we can implement multi-threaded managed code for WebGL, and and I’m sure yet which of them will be ready for production first.

4 Likes

Great news! I’m even more excited now. Aside from GC work, what else is there left to do? Are you still planning on releasing the player and editor separately? Sorry if I’m being too prying, I understand if you can’t really answer these questions. Just keep up the good work.