Introduction
The Name
In Greek mythology, Medusa is an entity which possesses the ability to turn others into stone. Such a nice name for a system which was created to render cliffs.
Application
As mentioned above, Medusa was created to accelerate the rendering of cliffs, later rephrased as a very CPU- and memory-friendly way of rendering always visible and fully static meshes.
Why new system
As you know from previous posts we already have Drake and Leshy. Why not to use them?
Drake
Drake uses a slightly modified Entities Graphics for rendering, which means you need to create an entity for each renderer, keep all data on the CPU, and every frame the data is sent to the GPU. There is also binning code, chunk upkeep costs, and other parts which make Entities Graphics and Entities themselves a very generic solution.
Medusa is the opposite of that; it's highly specialized. All data is baked into a file and, at scene startup, loaded into the CPU and sent a single time to the GPU buffers. Meshes and materials are directly referenced. There is no support for data modification.
Another significant difference is frustum culling. Medusa uses sphere-vs-frustum instead of the traditional box-vs-frustum used by Entities Graphics. All of this makes it very CPU-friendly.
Leshy
Leshy is a very nice system, but for cliffs, we need colliders to be present at a much larger distance than is required for Leshy. Also, cliffs are a landscaping tool, so they must be visible from a very, very long distance, meaning cell streaming from Leshy is not needed here. The last requirement is to not change the level designers' workflow; therefore, authoring must be done manually with MeshRenderers
.
From this, we can see that Medusa only requires the rendering part from Leshy.
How it works
Colliders
Colliders are always present for the scene; honestly, nothing changes here - we left them unchanged.
Rendering
Based on experience (and code) from Leshy, Medusa uses BRG with a few optimizations. The GPU side is immutable for a scene; once created at scene start, it persists with the same data and layout until scene unloading (when Medusa disposes). This means buffers are ideally linear and fully utilized. Batches are also ideal; for a given submesh, there is always just a single draw call.
Frustum culling is a highly optimized (SIMD in a multithreaded environment) sphere-vs-frustum, with no need for chunking, preprocessing, or any other fancy technique. This is because it's so fast that the entire cost of frustum culling and draw call preparation is hidden by HDRP's render graph main thread interactions.
Baking
As already mentioned, Medusa's objects are just plain Unity rendering primitives (LODGroup + MeshRenderers), so that data must be baked into an optimized data structure, which can be pushed to BRG without wasting user resources. It's also important to have the same data and representation in the final build as well as in Play Mode. To achieve this behavior, we used IProcessSceneWithReport
, which is super useful for ensuring consistency and parity between Play Mode and build, but it must be used carefully. Long baking times not only slow down releases but also affect the day-to-day work of everyone who needs to quickly check changes in Play Mode.
If you have a single system that needs to be baked and baking takes 1 second, then it's not a big deal. If you have 15 systems where each bakes in 1 second, that's 15 seconds of idle time for every entry into Play Mode. That adds up quickly: 15 seconds * 10 entries per hour * 8 hours is 20 minutes; that means for every 24 people in the team, one person-day is just idle waiting for Play Mode.
The data is baked into a separate file in StreamingAssets
, from where it will be loaded via AsyncReadManager
, a fast and parallel file buffer reading API.
Closing
That was a short one, but its brevity shows the strength of experience and solid foundations.
Reuse of already working, established, and well-known systems accelerates progress greatly. I would advise you to copy-paste relevant code and make modifications without haste. After that, compare the source system(s) and the new one to generalize where needed and possible.
Medusa is also a confirmation of the Data-Oriented Design mantra: "Know your domain." We could have stuck to cliffs rendered as common Drake renderers, but after a close look, we saw that there are few cases covered by Drake, and cliffs are so common that we can introduce meaningful optimizations here.
Top comments (0)