In June I stopped writing about Nova so I could write Nova… but now I’m writing about Nova again. Sorry for the delay, and for the very…dense nature of this blog. If you’re more into pretty pictures and less into a lot of technical talk, you might want to skip this one.
With that said, let’s get into it!
In June, I continued fixing any bugs I could find and continued building out more of the renderer. This included setting up the data for UBOs and handling the Minecraft lightmap properly. I also started on a CPU profiler to get a general idea of how well Nova runs.
Janrupf joined at the beginning of June and started working on moving Nova from raw MCP to Forge. Most of this effort was converting Nova's changes to Minecraft from a source code patch to Forge Mixins.
Barteks2x joined at the beginning of July and jumped into helping Janrupf convert Nova to Forge. He also updated Nova to run in Minecraft 1.12.2 instead of 1.10. That brought us into a better position to update to 1.13 when it became available.
Barteks2x also got chunk rendering working. I hadn't quite gotten that feature working, but Barteks2x was able to solve the issues and get the chunks drawing. Unfortunately, this introduced a bug where, on my computer, the GPU would freeze up after a second of rendering a world. Solving that would take me many weeks.
4D Klein Manifold (hereafter referred to as Klein) came in around the middle of July. He began working on getting the main menu panorama working.
We merged the Forge version of Nova into the Vulkan branch - essentially declaring that Nova was completely moved to Forge. During that merge we had to resolve a number of compatibility issues. Barteks2x and Janrupf develop on Linux, while I'm on Windows and Klein switches between the two. Unfortunately they had done a few Linux-specific things that needed to be resolved.
Janrupf then started setting Nova up for Continuous Integration (CI). CI lets us automatically compile Nova whenever the code changes. This ensures that the code changes we make won't horribly break Nova, and removes the need to manually compile Nova. I had wanted to set this up for a while, but it would have been much more difficult before we converted Nova to Forge.
Then came the task of merging the Vulkan branch into the Master branch. This was more tricky than one might expect because back in February, Klein had made some changes that made chunk loading much faster and more like vanilla - but made them when I was having doubts about Vulkan, so we ended up with two branches with different commit histories. Barteks2x took on the task of resolving all those changes, and did so very well.
With CI conquered, Janrupf began working on a standalone shader editor for Nova. Nova is much more complex and more involved than Optifine shaders or Bedrock shaders. Developers coming from those platforms will likely need a lot of help to become proficient with Nova, and a standalone shader editor can go a long way to providing that help. You can see and follow his work at here.
Another developer, Strum, also joined in July. He began working on the shader marketplace. The vision here is that shaderpack developers can upload their shaderpacks to a central location, then the shaderpacks can be browsed and downloaded through Nova, without leaving Minecraft or moving things to different folders. This will provide a lot of discoverability for shaderpacks, especially smaller packs that needs a popularity boost. You can see and follow his work here.
To help solve some of the issues that arose when merging Vulkan into Master, Barteks2x created a tool to capture the parameters to native Nova functions, then replay them in a JVM-free environment. The JVM is cool and all, but it does a lot of things that interfere with debugging C++ code. In the first week of its existence, his tool proved to be well worth it, time and time again.
But that prosperity was not to last. While Barteks and Janrupf were making wonderful progress, I did not have any such luck. A crash began rearing its ugly head. On Windows, after Nova was rendering 50+ chunks, it just…died.
I first noticed the problem as a segfault when updating the lightmap. I removed the offending code, and got a crash when updating a different resource. Removed that code, and now the crash happened when reading the GPU time queries for each frame.
The behavior was dumb. Were there multiple crashes? One bad one? I added a new thread that would poll the GPU to find out when each VkFence got signaled. It polled, and… things got weirder. I could see each command buffer be submitted, could see the fence get signaled… then a segfault somewhere inside vkWaitForFences.
This was dumb.
I dug into this for a week. I tried this and that, poked here and there…nothing. The GPU was just like “nah bro I’m out” in the middle of rendering a frame.
Vulkan isn’t the first-party graphics API on Windows 10, DirectX 12 is. On August 14 I made the decision to add in a DirectX 12 rendering backend for Windows 10. This was the first-party API on Windows 10, surely it would work better?
The work began. I would write the DirectX 12 backend for Windows 10, Janrupf would make the Vulkan backend. By this point Barteks was back to focusing on CubicChunks and unfortunately couldn’t give more time to Nova
Janrupf did a lot for the architecture of Nova in the last half of August. He realized that Nova shouldn’t have its own logger or load its own config file, that all should be provided by whatever application Nova was embedded into. Additionally, he pushed for “add a DirectX 12 backend” to become “add a DirectX 12 backend and also rewrite the Nova backend”, which I agreed to. The Vulkan code would have to be mostly replaced anyways; I was trying to get things done quickly and made far too many compromises
During September, I continued working on the DirectX 12 code, and Janrupf continued working on the Vulkan code. At one point I had the bright idea to make a command buffer-based abstraction over both DX12 and Vulkan. That was a horrible idea, because while DX12 and Vulkan are similar, they aren’t similar enough to have a single interface. Bleh.
In mid-September I decided to convert Nova to a job-based threading model built on top of fibers. This meant that Nova could take advantage of all the cores in your CPU, and they it could do so very efficiently. There were a number of growing pains as we adapted to this new style of programming, but ultimately we learned.
We started working on loading shaderpacks in both Vulkan in DirectX 12. We made a good bit of progress, but Janrupf had to go back to school and my girlfriend came to visit for three weeks (one week in October) so not much happened in the second half of September.
I added all kinds of validation and debugging information to the shaderpack loading code in October. Shaderpack developers are going to have to re-learn a lot of things to take full advantage of Nova, and the better the error messages I can give them are, the easier the transition will be. I also spent a lot of time fleshing out the shaderpack loading code for both Vulkan and DirectX 12. Janrupf was busy with school, which meant that the Vulkan code fell on me. The work to load shaderpacks and create the API-specific resources that each one needed was straightforward, if a little tedious.
What was not straightforward was transpiling shaders. Nova accepts shaders in GLSL (which is compiled to SPIR-V) or in SPIR-V. DirectX 12 accepts shaders in HLSL (which is compiled to HLSL bytecode), or HLSL bytecode. Additionally, a few concepts that are crucial to Vulkan shaders have no equivalent in DirectX 12 shaders. This meant a pretty significant amount of work on my part to perform the translation.
Additionally, on October 22 I started a new job. This was pretty cool, but meant that I’d have less time for Nova.
Rewriting the Vulkan code and adding in DirectX 12 code will take plenty of time, but it’ll be time well spent. Nova will end up with a well-organized code base that can be easily extended as more and more features get added to the code.
Nova won't be updating to 1.13 until Forge does, and Forge won't work with 1.13 for a while - they want to take some time to improve their code quality. This gives us a lot of time to make Nova amazing before we have to handle all the changes that Minecraft made for 1.13. We're excited to be starting to realize the vision of Nova, and we hope that you're excited, too!