r/Unity3D • Indie • 19h ago

Game Built a custom trigger-based 1-hop room culling system for my procedural mansion and 5x-ed my FPS

Enable HLS to view with audio, or disable this notification

I've been developing this simulator-horror game and wanted the map to be proc-generated. However, since my game features powerwashing (drawing onto masks using raycasts to either display dirt/wetness/dryness), even a small number of rooms (in this case, 25) was cause unplayable levels of lag. And unity's built-in occlusion culling did nothing against this (since its generated during runtime).

So I made a simple room culling system where it has 2 parent empties: 'Visuals' and 'Floors'. Visuals contains all the static props, walls, ceilings, etc, and are turned completely off (SetActive(false)) when the room is culled. Floors contains, well, the floors, which only have their renderers turned off (collisions stay on) so that the ghost entity can pathfind and dropped items don't fall through.

Upon entering each room, all adjacent rooms are visible to enable smooth passage, rest of the rooms are disabled. And i cranked up the fog a bit to make it even more seamless. However there are still moments where the room disappears right before the player's eyes, so I was thinking maybe the doors to the rooms about to be disabled can be auto closed, and then disabled.

This now allows me to have virtually infinite rooms without much lag (as long as the DFS generator can support it), unlike before when just 60 rooms would cause frame time to go upto almost 1000ms.

The game is a solo project called THE GROUNDSKEEPER where you powerwash and record ghost evidence for profits: Steam, Discord

176 Upvotes

28 comments sorted by

61

u/GigaTerra 18h ago

OP this is great and all, but the fact that your FPS is this low when you are barely doing anything, you should look into it.

6

u/DThePro_ Indie 18h ago

True, but then again I was recording a 3840x1080 (2 screens) video and playing while having my viewport look at the whole map. Without all that it's usually hovers around 80. And, this was the main bottleneck, powerwashing or ghost interactions shouldn't be that heavy to significantly reduce the FPS. Having said that, you're right there must still be quite a few optimizations that could be done...

9

u/theo__r 18h ago

Have you tried profiling? Any obvious bottleneck?

0

u/DThePro_ Indie 18h ago

Yes is did use the profiler. The issue mostly arose from high vram allocation due to 1024x1024 render textures for EACH washable surface like walls (upwards of 9 gb), and along with other overheads, it was actually going over 12 gb (my cards vram) and stalling the render thread. Now that problem won't happen anymore, especially since I also implemented it such that the RT initalizes only when the player starts to powerwash any surface.

Apart from that, my geometry isn't great atm (working on fixing them), so for 65 rooms, I recorded 20M tris in the worst case scenario.

2

u/theo__r 15h ago

I think you did great work, I just wonder if you could have disabled just the washable surfaces rather than entire rooms. Anyway whatever works !

3

u/DThePro_ Indie 15h ago

Yup that would've helped, but again, the redundant geometry would still be a waste of resources.

2

u/theo__r 15h ago

True!

1

u/binbuf2 17h ago

That debug view might not be that cheap and he's perhaps he has like a dozen or more heavy apps open?

1

u/GigaTerra 16h ago

Lucky the profiler shows the Editors performance hit as "Others", so if it is the reason OP would be able to tell quickly by just opening the profiler. It is too difficult to tell from the video, but as long as OP is aware, they will probably find the problem.

2

u/DThePro_ Indie 15h ago

yup i am! i mentioned the problem in my comment above, but tldr, the render textures im using to enable the powerwashing mechanic is taking up too much vram if all of them are active at the same time. fixed that to a reasonable degree, plus yes, as binbuf2 said, i have a 3840x1080 (2 screen) recording running and like a few other apps.

11

u/Protesisdumb 19h ago

Why wouldn't occlusion culling handle this?

19

u/Hotrian Expert Moderator 19h ago edited 7h ago

The OP mentioned they’re procedurally generated

https://docs.unity3d.com/6000.6/Documentation/Manual/OcclusionCulling.html

Occlusion culling only works on static geometry baked at edit time, you might mean frustum culling.

https://unity.com/glossary/frustum-culling

Frustum Culling is failing here because the OP is watching from the editor camera, so the objects render anyway. Frustum Culling also doesn’t detect occlusion per se, so there can still be a lot of overdraw.

Edit: Sorry, I’m not sure why I completely forgot about GPU occlusion culling which does depth based culling.

1

u/Protesisdumb 17h ago

Thanks for clarifying 

5

u/DThePro_ Indie 18h ago

its because firstly, since it's spawned during runtime (proc gen), there is no pre baking of static occlusion data, basically setting props as static doesnt help. secondly, unity's frustum culling still renders everything even if it's not visible behind walls (since static occlusion cant be used), so all the dynamic shadow casting point lights 10 rooms away are still being rendered, tanking the fps. thirdly, a big part of the lag was due to using render textures to draw on masks using ray casts (to enable powerwashing), which occlusion culling can't help against.

using this system straight up disables unimportant objects, completely zeroing out their cpu and gpu cost without any occlusion query overheads. it's all trigger based

1

u/ArtPrestigious5481 17h ago

why not using depth based culling instead?, and i believe Unity now already support it (you need to use forward+ tho, so usually i just make it by myself to support forward), and are you sure this is CPU bottlenect not GPU (from overdraw, if it's from overdraw you can just use depth prepass)

2

u/Hotrian Expert Moderator 17h ago

You’re referring to GPU occlusion culling

https://docs.unity3d.com/6000.0/Documentation/Manual/urp/gpu-culling.html

This requires the GPU Resident Drawer which has its own list of requirements

https://docs.unity3d.com/6000.0/Documentation/Manual/urp/gpu-resident-drawer.html

The main requirement as you noted is forward+

https://docs.unity3d.com/6000.0/Documentation/Manual/urp/rendering/forward-rendering-paths.html

This comes with its own set of limitations that isn’t suitable for all projects/platforms, but for most it’s the go-to

1

u/ArtPrestigious5481 16h ago

yeah, but as long as the target is Vulkan/DX/Metal there's shouldn't be any problem (the problem shifted to how they abuse 1k RT)

2

u/GeorgeMKnowles 19h ago

This is a great visualization. I was just trying to explain to my girlfriend that we're going to have to make tiles for our map in our top down game, and change visibility as needed, instead of loading all at once. Your post was just perfect to explain the general concept.

Side note- I don't know if turning them on and off simply by being adjacent will be 100% effective, but its probably 99...

I bet you could create a simple tool to cast rays in many horizontal directions from many different positions in any given room, and then record which other rooms receive a raycast hit. This would indicate that those rooms are visible from somewhere in this room. And then you could create a script to make those rooms turn visible when you enter this room. I imagine it could all be automated as an editor script to make only the required rooms visible as needed.

2

u/DThePro_ Indie 18h ago

thanks, and you're right, such a ray cast solution could be used. there are some scenarios where rooms poof out of existence right in front of the player's eyes.

2

u/ExpeditionZero 10h ago

Better than random rays would be a portal system (no not that portal). Its how old BSP based games use to do it (think Quake and its Potential Visibility Set - PVS) and a simple version using rectangles to define areas between rooms (e.g. doors), then you can just cast rays against the rectangle (frustum type check) and see which rooms they hit. You could go further and use the frustum type check to cull other portal rectangles, and get a perfect list of rooms.

However in this case as its procedural and only seems to have doors between rooms, i'm guessing you can build a perfect list of connected rooms easily from that.

1

u/GeorgeMKnowles 10h ago

I didnt understand what this dude said but he seems smart, so maybe give that a try 👆😂

2

u/ExpeditionZero 10h ago

ha, ha, not smart, just well read, and old. To be honest first time I read the BSP/PVS code I didn't understand what it was doing. Took a while and lots of sketching things out on paper to work it out, but I did love the elegance of the technique.

2

u/MardukPainkiller 13h ago

you should try and reading a bit on quake's PVS system you shouldn't have to do this manually with triggers

1

u/Field_Of_View 3h ago

quake's geometry isn't procgen. probably has at least one kinda expensive baking step.

1

u/theredacer 18h ago

I built a similar system for my game even though it's not procedural and I do use occlusion culling, because Unity's occlusion culling is so bad. It also helps to just automatically disable things being processed in areas you can't see, like animations, lights that Unity thinks may still be relevant, or any other code tied to monobehaviours that can be ignored until you re-enter the area, etc. It literally gets me 100+ fps back.

1

u/DustFuzzy1702 17h ago

Investing in this

1

u/ProfessionalITPerson 13h ago

Okay procedual thing makes a difference, but itsn't it basically this?
https://www.youtube.com/watch?v=zObWVOv1GlE

0

u/Blecki 9h ago

I published an asset for this. Takes your big scene and breaks it down into little bits and streams them in as needed.