Dreamscape Optimization Ideas

Hey all- Will suggested I open this up for ideas and discussion, and this seemed like a good place for it. In a nutshell:

Dreamscape is the third in a series of games that started on iOS several years back. With this one, I want to go much bigger. As such, the environment is huge and loaded with details.

I knew going in that optimization would be a challenge. I’ve been debating going all-in and switching to Unreal and developing for console, BUT I am still very much open to ideas (I really enjoy the freedom and share-ability of PlayCanvas).

I would love to entertain and implement any optimization ideas, so bring them on! A few caveats:

I know I am using a lot of heavy textures and models. Since I didn’t know if I was building for Playcanvas or console, I built heavier, knowing I could pare back as needed, but couldn’t add detail back in later if I wanted to go big.

I am willing to make SOME sacrifice for framerate and playability, but I’m building this the way Christopher Nolan makes movies-- for the best possible viewing experience, not optimized for lower end. Thus, I am willing to sacrifice play on mobile, for example, unless some miracle is possillble.

With all that in mind, here’s the link: PlayCanvas 3D HTML5 Game Engine

Let’s hear it, and thanks in advance!

1 Like

You have a lot of scripts that do work in update, for example, LOD. You might be able to move this to a timer that only runs 10 times a second without changing any perceived delays. You may have other scripts that don’t really need to update 60 times a second.

There might be situations were one or more web workers can be used offload some of the game logic. Not sure if this is supported by PlayCanvas.

BTW, for female players, they might want to access the Ladies bathroom to avoid breaking the illusion.

1 Like

Thanks for kicking off this thread, @gnormanlippert.

So I think the biggest problem you’re facing right now is the stuttering that occurs as scene elements are streamed in. At first, I thought this was purely down to shader compilation. But on inspection, this seems to be because you initiate a rebake of your whole scene’s lightmaps very regularly (maybe when models are streamed in…not entirely sure). Because the scene has been gradually growing and growing, the rebake is now very expensive.

You can actually limit the rebake to a specified array of entities (param 1 of the bake function).

But an alternative is that you don’t use the runtime lightmapper at all. You are using clustered lighting in the scene, so you could consider relying on that for all your lights.

I think it’s best that @mvaligursky follows up with his own thoughts on this and provides more details and suggestions. He’ll post a follow up in the morning. So don’t make any changes based on what I write here until he replies! :smile:

1 Like

I love the suggestion about the women’s restroom! Truth is, the Tiger is a little like Moe’s in the Simpsons: so few women patrons ever come in that there literally isn’t a bathroom-- the door is just a facade, heh!

Definitely your current larger bottleneck is the lightmap baking. Considering the baking quality is pretty much equivalent to the runtime lighting (unless you use soft baking options), as Will mentioned, I’d try and rely purely on the runtime lighting. It will automatically use up to 255 of the nearest lights.

If your point / omni lights need shadows, by default the clustered lighting would try to give all of those part of the shadow atlas. That is expensive for many lights, as all shadows would by default be rendered each frame. But there’s a system where you can specify the allocation strategy for the atlas, so for example you’d have nearest 4 lights with high res shadows, further 10 lights with low res shadows and the rest won’t have shadows. You can see this in this example: PlayCanvas Examples
Set the split to 7 shadows and see how the lights away from the camera don’t have shadows.
Read more about it here: Clustered Lighting | PlayCanvas Developer Site

Similarly, you can update shadows at a lower frequency, this also applies to cascades of the main directional shadow map. The nearby cascade you can update every frame, but the far one perhaps every 3 frames.

1 Like

Thanks! I will look into these ideas later today, but in the meantime: would it make sense for me to grant editing access to you and Will and anyone else who might want to tinker under the hood?

Personally, I tent to mess with people’s projects in a fork.

1 Like

Unrelated but problematic: Any ideas why the editor might get hung up on one object having lightmapped ticked, but not having a UV1, such that the editor re-runs a lightmap every time a entity is manipulated in the editor? That started happening yesterday and it’s making the scene extremely unwieldy to work in. (NOTE: I have combed through the heirarchy and can’t find the entity the lightmapper is warning about)

@KPal - any idea what could be happening here?

Did you initially have the lightmapper set to Auto Bake?

I’ve enabled and disabled that as needed, but it should have been off. Currently, the option doesn’t appear. When I mouse over the lightmapper button it simply shows a popup that reads: “UV1 is missing on some models. Please upload models with UV1 or use auto-unwrap”, and the lightmapper remains orange (enabled). Clicking it does not disable it.

The lightmapper autobake option has now moved under settings so just verify it is set as intended there. Also could you please share a screenshot of what you described above?

https://i.ibb.co/bWQgv3y/Screen-Shot-2024-03-26-at-11-15-01-AM.png

That’s what I am seeing. BUT I did find the auto rebake option in the settings and disabled it. Weirdly, that didn’t fix the issue, so now I assume it isn’t related to lightmapping.

What happens is: whenever I edit an entity (move, rotate, scale, etc) the editor freezes for a few seconds before further editing can continue. Any ideas?

Try to use the Profiler (Performance tab in Chrome dev tools), run it before using the record button (left one), then trigger that slowness, and then stop recording. See / share what it shows.

1 Like

SO turns out the problem doesn’t occur in Chrome. I had been working in Firefox. On Chrome there is no lag or freezing. So I guess that’s what I’ll stick with. Thanks! Onward!

In other news: I edited the LOD scripts so they only update once per second, which seems to be helping some…

1 Like

@KPal hmm, is there a way to disable auto-rebake? It looks like, at least in this project the stall comes from the large amount of objects in the scene that needs a lightmap rebake, which is triggered automatically whenever something is moved.

I did manage to disable auto-rebake in settings-- and I switched from Firefox to Chrome. That seems to have fixed the issue. Now I am digging into the lighting ideas listed above… Onward! And thanks!

Well, the thing is that it should not matter whether you use Firefox or Chrome. If you have a chance to record a performance log under Firefox with the lag, that would help. I tried it under Firefox, and the only stutter I see (apart from initial loading and shader comp) is the Lightmapper rebaking, which is the same in both Firefox and Chrome for me.

Happily/frustratingly, the issue seems to have resolved itself even in Firefox. I reopened the editor there and everything runs fine. PROBABLY I just needed to refresh the browser after disabling auto rebake.

As I begin to dig into the light-baking options, a quick explanation for why I have implemented secondary light-bakes as the game runs:

Basically, there are a lot of LOD entities that are disabled (be design) when the initial light-bake runs. These entities get swapped in as the Player approaches, but they are not properly lit.

Thus, I created a second LOD script that triggers a new lightbake every first time a lightmapped LOD object is swapped in. Since there have come to be a lot of such entities, the light-bake occurs far too frequently.

Is there a way to simply tell the editor to lightbake ALL entities, regardless of whether they are enabled or not, upon initial run? That definitely seems like a far better option, yes?