My Journey with Physics
Jolt vs. PhysX (In my opinion)
Physics is important. Both in real life and in games.
Physics is also interesting, both in real life and in games.
Anyone who knows me is probably aware that one of my jobs at Studio Cherno is developing the physics engine in Hazel.
In fact that was how I first started working on Hazel, and how I got to know Yan. On September 11th, 2020 I made my first commit to the proper Hazel repository.
And that was the first commit that introduced 3D physics in the engine, because I had been put in charge of integrating the PhysX library with the rest of the engine.
I don't know the exact date that I started working on that PhysX implementation because I was working of a fork of the engine, and that fork sadly doesn't exist anymore.
The start of this post will be a semi-short overview of my thoughts on the physics system in Hazel, and how it has evolved, from the initial implementation, all the way to the current rewrite that I'm working on.
I'll talk about Jolt later on in the post, so if that's what you're interested in just skip the first half of this post.
The Initial Implementation
While I'd say I'm quite comfortable programming in C++ nowadays that definitely wasn't the case back when I first started working on Hazel.I had no real experience with C++ at that time, my only real experience was working in Java and C#, and as a result my initial implementation wasn't great, but it worked (for the most part).
If you're interested in seeing what the physics system looked like back then you can check out this video that Yan made showcasing it: 3D Physics! // Hazel Engine Dev Log
But to cut this part short: it was terrible, the actual C++ code was terrible, 90% of the physics engine was in a single, convoluted, messy file. And the way that I actually used the PhysX API wasn't great either. Honestly I'm still amazed and surprised by the fact that Yan allowed me to continue working on Hazel after that initial implementation. And I still can't believe that he hired me to work professionally on Hazel.
Over the next couple of months I worked to improve the design and functionality of the physics engine, and over time it got better, and so did my ability to write decent code.
The initial implementation stayed in the engine for a lot longer than it should've, but eventually I went on to completely rewrite the system from the ground up.
The Next Version
After working on the initial implementation of the physics engine I went on to work on some other systems in Hazel, including the Asset system and the Scripting Engine (I'll make a separate post talking about the Scripting Engine later on...).The first major rewrite of the physics engine was merged to master on the 3rd of July, 2021, and it improved many things both in terms of API design and functionality.
The biggest change was that I broke up the gigantic wrapper around PhysX into multiple smaller files. I also went on to improve the performance of the overall system.
I think that the quality of my code improved significantly during the rewrite, and my understanding of game physics improved drastically as well. During my initial implementation I had a really weak understanding of how physics in games worked, best practices, etc...
I think the most important lesson I learned with this rewrite was that I needed to take my time when designing the API of a system. My philosophy was, and still is to some degree that you should first get the most basic version of the system up and running, even if the result is a buggy and badly written API.
But after the rewrite, while my philosophy was pretty much the same, I realized that I should still pay attention to the overall design and code quality of the system, that way you won't have to completely rewrite the entire system when you come back to improve upon it.
I still believe that getting a minimum viable product (MVP) up and running before trying to make it good, and optimized is the way to go for the most part. But nowadays I try my best to maintain good code quality and API design while getting the basics up and running.
After I had finished the rewrite of the physics engine I moved on to take charge of, and rewriting, the scripting engine in Hazel. This is something I regret somewhat today, since it meant getting up close and personal with the Mono library...
It would be quite some time before I'd do the next major rewrite of the physics engine.
Introducing Jolt
And that brings us up to the current rewrite of the physics engine. The biggest change with this rewrite is that we're now working on integrating a completely different physics library into Hazel, Jolt.The Premise
Before I talk about the current rewrite I think it's important to understand how I found out about Jolt, and why we decided to implement it into Hazel.I'd heard about Jolt quite a while before I decided to give it a go, some people on Yan's Discord server had talked about it a few months before I started testing it, but I didn't really think much of it at the time, mainly because I was a bit burned out from working with PhysX.
But on July 16th, 2022 I decided to check it out, and I was impressed with what I saw. And so I decided to try it out.
Initially I was planing on only working on the Jolt integration during the weekends (I was employed by Studio Cherno at this point, so I didn't want to waste Yan's money), and after talking to Yan about it he was on board with giving it a go.
Jolt also had certain benefits over PhysX that made me really excited about trying it, for an example Jolt is licensed under the MIT license, so it's completely open.
It's being actively developed in an open source manner, whereas the PhysX repository wasn't actively updated anymore, mainly because of PhysX 5, which didn't have a standalone SDK available at the time, you had to use Nvidia Omniverse if you wanted access to it.
Jolt had also been used by Guerrilla Games when they made Horizon: Forbidden West, which is a great game by the way.
Jolt is a newer library compared to PhysX, Jolt most likely started development sometime in 2018 (I could be wrong on this), whereas PhysX started it's journey back in 2001 (although it wasn't PhysX back then, but a physics engine called NovodeX).
Either way Nvidia had been developing PhysX since 2008, and the API isn't written in a more modern way.
Jolt on the other hand was developed from the ground with multi-threading, and multi-core processors in mind, and the API is written using modern C++.
Now, that's all well and good, but in the end what actually matters is the performance. There's no point in using Jolt over PhysX if the latter is going to give us the best performance.
So I decided to get a basic implementation of Jolt going side-by-side with PhysX so I could do some profiling.
The Initial Tests
Immediately after diving deeper into the Jolt API I fell in love with it, it was much better written than the PhysX API in my opinion, it required me to be more explicit about what I wanted from it, to the point where I like to compare Jolt vs. PhysX to OpenGL vs. Vulkan in terms of API design.If you want to take a look at my initial profiling result you can find the spreadsheet and graphs here: Initial Profiling Sheet
And if you want to see my presentation that I made after spending some time with the two APIs you can find the slideshow here: Slideshow
I will say that the accuracy of the profiling results is questionable nowadays, but I have found that Jolt is generally capable of getting better performance overall, so the results aren't completely incorrect, but they might not reflect the true difference between the APIs anymore, especially considering that PhysX 5 is now available separately from the Omniverse SDK.
Regardless, the tests I performed convinced me that Jolt was worth integrating. This doesn't necessarily mean that we'll get rid of PhysX though, I would like to upgrade the version of PhysX that Hazel uses from PhysX 4.1 to PhysX 5.1, but that's for a later day.
But that brings us to the actual physics engine rewrite, and my comparison of Jolt vs. PhysX 4.1.
The Current Rewrite
The first commit of the current rewrite was made on the 17th of July, 2022, and this time I had to account for two physics libraries instead of just one.This is by far the most well written system I've written in Hazel to date. It's culmination of my entire journey with C++ and game related physics systems so far.
Now it definitely could be improved upon even more, but compared to all my previous work in Hazel it's the work I'm the most proud of.
I took a lot of inspiration from Jolt when I was designing the new API, which means that PhysX might be slightly less optimally implemented, but not in any way that actually affects the performance noticeably.
I simply prefer the way that Jolt is architectured.
I had a few goals in mind that I wanted to achieve with the current rewrite:
- Build a scalable physics API for Hazel that could support multiple internal APIs
- Fix some of the weirder aspects of the old system, such as the C# API integration
- Integrate physics profiling more closely with the engine (resulted in the new "Captures" system)
- Optimize the simulation performance as much as possible
- Build a base that can be expanded on at a later date, this is important for supporting joints and ragdolls
At this stage in the rewrite I feel that I have achieved most of these goals, at least to a small degree. It'll still be quite some time before we can get ragdolls implemented in Hazel, but at least now we have a good base to build that system on top off.
That brings me to the end of this post: my comparison of PhysX vs. Jolt, my thoughts on the two APIs, and why I prefer Jolt.
PhysX vs. Jolt (And why I prefer Jolt)
Alright, we've made it to the final part of this post. The Comparison.I'll list the things I think Jolt does better, and then the things I think PhysX does better. Keep in mind that these are my opinions, this comparison isn't meant to determine which API is objectively better, that's impossible to do.
Things Jolt Does Better
- Upfront about allocations. Jolt expects that you tell it how much memory will be needed for the simulation, which definitely results in better performance since Jolt won't be reallocating a lot of memory during the simulation. (Jolt does provide a way of doing allocations dynamically, so upfront allocations isn't a requirement)
- Better multi-core support. Out of the box Jolt will utilize multiple cores very well, whereas I've found that PhysX doesn't utilize multiple cores all too well. Jolt was designed with this in mind whereas PhysX wasn't.
- Explicitly reference counted objects. When you create a new object in Jolt you'll most likely get a "Ref" instance back. PhysX does utilize reference counting, but it's not as apparent as it is with Jolt because PhysX returns pointers to most objects.
- Multiple collision shapes per body. Jolt handles this by providing a "compound" shape, which you construct from multiple other shapes, whereas PhysX just lets you add an arbitrary amount of shapes individually to any body. I'm not entirely sure how PhysX handles this internally, but I do know that Jolt computes these shapes into a single more optimal collision shape, meaning that collision tests are most likely more optimal.
- Better Multi-Threaded access. Jolt was designed with this in mind, and most methods in Jolt supports being accessed by multiple threads at once, whereas PhysX requires you to make use of a "Task" system, which is poorly designed in my opinion.
- Jolt achieves a higher simulation fidelity / better simulation quality out of the box. Jolt gives us some really good simulation values out of the box, whereas PhysX requires us to play around with a lot of parameters in order to get a high quality simulation.
Things PhysX Does Better
- PhysX has better simulation performance out of the box, whereas Jolt requires more careful setup and usage of the API in order to achieve good performance. Jolt does however gives more opportunities for optimizations, and can achieve better performance overall, but it does require more work
- PhysX supports GPU-based simulation by utilizing the CUDA architecture, sadly this is limited to Nvidia GPUs only
- The PhysX Visual Debugger (PVD) supports recording the simulation over the network, Jolt requires us to record the simulation to a file
- The PVD doesn't just visually record our simulation, but also records the parameters of the simulation at any given moment, allowing us to inspect the parameters more easily. Jolt on the other hand only records the simulation visually.
- PhysX offers us more control over mesh collider cooking, giving us more parameters to mess around with
- PhysX has far better documentation overall, since it's an older and more widely used API there's more resources on it online, whereas Jolt lacks online documentation. The developer of Jolt is very active on the GitHub repository though, and has been very helpful with any issues I've encountered, or any questions I've had about the usage. This isn't the case with the PhysX developers.
- Achieves better performance when adding a lot of new bodies during the simulation. Jolt requires us to manually optimize the broadphase structure, and doing that when there's already a lot of bodies in the broadphase can result in slowdowns temporarily.
Alright, those are my thoughts on which API does certain things in a better way than the other. Now, why do I prefer Jolt over PhysX? Isn't PhysX a more mature API? Well, yes, it is. But that doesn't matter too much to me.
There's a few reasons why I prefer Jolt:
- More modern API, utilizing more modern versions of C++
- More well written, and less confusing API
- The developer of Jolt is more invested in communicating with the people using Jolt, whereas interactions with the PhysX developers feels more corporate (which is understandable)
- Jolt requires us to be more explicit about what we want, which in turn makes more obvious what's going on internally
- Jolts development is heavily influenced by the needs of Guerilla Games, seeing as it's the physics library that they use for the games (starting with Horizon: Forbidden West), whereas PhysX isn't really influenced by games that much anymore. Especially considering that PhysX is used for a lot of non-video game areas, they've recently started focusing more on simulations for VFX, which means it ends up getting bloated with features that aren't needed, or useful for video games.
These are the main reasons why I prefer Jolt over PhysX, there are many more reasons but I won't bother listing all of them here (we'd be here a while). I want to make it clear that all of the things I've talked about are my own personal opinions, and aren't meant to sway you in any particular direction.
If you're considering messing around with physics please do some research on your own to determine which library suits your needs the best.
I know that I kind of rambled on a lot in this post, but these are my thoughts and opinions so I just sort of dumped them out. That'll be a recurring theme for my blog posts going forward as well.
Anyways I'm going to sign off here, I may talk more about physics in the future, so if that's something you're interested in feel free to keep an eye out! I'd like to talk about the scripting engine in Hazel soon, and why I partially regret taking the task of developing and maintaining it, so keep an eye out for that.
Anyway, thanks for reading my very rambly post!