Logic World Wednesdays: A Big Ball of Inverters

by @MouseHatGamesDeveloper1 year ago

Framerate go brrrrr - Jimmy

I’ve spent the last several weeks working on major performance improvements for Logic World update 0.91. The primary goal of these improvements is to enable much bigger builds by shortening loading times, increasing framerates, and lowering RAM usage for worlds with a very high number of components.

I’m finally at the stage where I can demo these performance improvements in-game! Here’s a video showing off a test save that contains 1,000,000 inverters and 10,000 wires – something that would be completely impractical in 0.90.

I was almost at this point last week, but I ran into two major roadblocks, the tales of which I will presently regale you with.

Annoying roadblock #1: shadows not working properly

I spent forever getting my instancing shader to render shadows, as I showed off last time. Imagine my dismay when I integrated the shader with Logic World and the shadows broke. After extensive investigation, the issue turned out to be with using multiple meshes. Instancing a single mesh many times, as I was doing in my demo scene where I developed the shader, would result in correct shadows. However, as soon as you introduced a second instancer with a different mesh, the shadows would break.

This made no sense and was extremely confusing. I’m still new to working with shaders & general graphics tech, so trying to fix a bug this outrageous was a daunting task. Nevertheless, I took the plunge; I read up on how shadowmaps work and tried everything I could think of to make them look normal again. After three days of this I had eliminated dozens of potential solutions, but was no closer to understanding the cause of the issue. Then, on a whim, I updated Unity to the latest patch release… and the bug was fixed!!!

ALL ALONG IT WAS A UNITY BUG AND NOT A JIMMY BUG. Three days trying to fix it when it was completely out of my hands, and all I had to do was press “update”. Well, lesson learned: in the future, when my code doesn’t work, I’ll be much quicker to blame someone else.

Annoying roadblock #2: invisible thumbnail renders

Logic World renders thumbnails for the items on your hotbar. This thumbnail-rendering system broke when I made the switch to GPU instancing. The world renders itself using Unity’s Graphics.DrawMeshInstancedIndirect function, but this unfortunately is incompatible with Camera.Render, which is what we were using to render the thumbnails. Camera.Render executes in the middle of a frame, but DrawMeshInstancedIndirect only actually instances the meshes at the end of a frame. Therefore, those instances couldn’t be seen by Camera.Render, and all the thumbnails were empty images.

I did some reading, and I figured out that I should be able to instance the mesh during Camera.Render using something called camera command buffers. I wrote the code to do this, but it didn’t work. I did some more reading, and it turns out that camera command buffers are not compatible with Unity’s Scriptable Render Pipeline, which Logic World uses.

So instead I started mucking around with Scriptable Render Features, which as far as I can tell are the only way of executing command buffers during Camera.Render in SRP. I did get the mesh instancing to work with this, but I couldn’t figure out how to make it work with any lighting, so all the thumbnail renders were dark and ugly.

Finally I gave up on doing things in a good clean way and implemented a crappy hacky solution. The only way I have of properly rendering the objects is with Graphics.DrawMeshInstancedIndirect, but that doesn’t work until the end of the frame. So, we’ll simply wait until the end of the frame to render the thumbnail. This unfortunately means that there will be one frame of delay between when we request a thumbnail and when it gets rendered; it also means that we can only render one thumbnail per frame, as otherwise the two items would overlap in the render. However, I believe I can mostly work around these issues by beginning thumbnail renders as soon as the game starts, so that every thumbnail you need is already rendered by the time you load into a world.

Overall it took about two days to fix all the issues with thumbnail renders. Bleh. While I was there, though, I took the opportunity to up the quality: thumbnail renders are now anti-aliased. It’s a subtle change, but a nice one. You might be able to notice the difference in the above video; the edges are much softer and less jagged.

What’s next?

After I’d finished with the above two issues, it’s thankfully been relatively smooth sailing to integrate the new rendering tech with Logic World. I’ve carefully checked everything, and all the visuals in 0.91 are now identical to the visuals in 0.90, down to every channel of every pixel. Objects look exactly the same, but they’re much faster to render in large-world scenarios.

I’ve still got a few more things to finish up with the optimizations, but I’m finally almost done with them. Once they’re finished, I’ll do a hefty round of bugfixing, and then I finally get to work on new features again!

The hardest part of 0.91 is finished, and the update will be in your hands soon.

RSS icon
@TheWildJarvi1 year ago

Will this break old saves or should it be safe with them?

@JimmyDeveloper1 year ago

Now that the game is released, we’ll always ensure forwards-compatibility of saves. I am planning to change the file format of saves in this update, but there will be a converter. Anything you build in 0.90, you can continue working on in 0.91.

@AIdude1 year ago

The demo looks amazing. Good job Jimmy!

Also hacky solutions just work.

@JimmyDeveloper1 year ago

Yep… one should try to avoid hacky solutions, but sometimes nothing else is practicable, and you just have to make the best of it.

@Ecconia1 year ago (edited1 year ago)

Jimmy: Wait its a Unity bug?!
Unity: Always has been!

Even more looking forward!

So here are a few questions for this LWW:

  • Regarding the checklist/todos:

    • Collision and instancing are (almost) down!
    • What about inport/export of subassemblies, is that already done?
    • For when is subassemblies in hotbar scheduled?
  • So RAM usage decreased due to less colliders and no longer having to store meshes?

  • Didn’t you plan to update Unity in 0.92, or is there a pending major version and you updated to a minor version?

@JimmyDeveloper1 year ago (edited1 year ago)

What about inport/export of subassemblies, is that already done?

For when is subassemblies in hotbar scheduled?

The backend for these is almost done, I mostly just have to code the UI for it.

So RAM usage decreased due to less colliders and no longer having to store meshes?

Yep! The 1 million inverters save uses ~4GB of RAM in my dev version.

Didn’t you plan to update Unity in 0.92, or is there a pending major version and you updated to a minor version?

You remember correctly, and you assumed correctly :) for 0.91 I’ve updated from Unity 2019.4.17f1 to 2019.4.37f1. For 0.92 we’ll be updating to 2021.3.