Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vulkan Mobile: Decals pop away when there are more than 7 decals on a mesh resource (instead of the expected 8) #70231

Closed
Tracked by #57284 ...
elvisish opened this issue Dec 17, 2022 · 19 comments · Fixed by #70929

Comments

@elvisish
Copy link

elvisish commented Dec 17, 2022

Godot version

4.0 beta 8

System information

Windows 10

Issue description

Adding some decals to a scene works well, until trying to rotate the camera or move around them.

2022-12-17.23-20-30.mp4

Steps to reproduce

Create a basic decal with a texture and leave everything at default.
Add the decals to the scene.
Rotate the camera and move around.

Minimal reproduction project

min_rep_decals.zip

@Calinou
Copy link
Member

Calinou commented Dec 17, 2022

Related to #49639.

@elvisish Please upload a minimal reproduction project to make this easier to troubleshoot.

@Calinou Calinou changed the title Decals in 4.0 pop away when rotating and moving camera and get stuck Vulkan: Decals pop away when rotating and moving camera and get stuck Dec 17, 2022
@elvisish
Copy link
Author

elvisish commented Dec 18, 2022

Related to #49639.

@elvisish Please upload a minimal reproduction project to make this easier to troubleshoot.

min_rep_decals.zip

Try walking (WASD) rotating the camera (mouse) and clicking the mouse to shoot at surfaces (sorry they're misaligned on the walls, haven't had time to fix that).

@elvisish
Copy link
Author

elvisish commented Dec 19, 2022

Okay, I think it might have something to do with it being added to a large mesh as it doesn't seem to happen on smaller objects (and trenchbroom imports levels as one big mesh). I'm guessing it's an optimization issue? It still happens on small meshes, especially if they're overlapping or close to each other.

@elvisish
Copy link
Author

Still broken on latest beta 10, decals are totally useless for me right now (since they're a purely visual effect, they haven't got any use other than displaying correctly).

@Calinou
Copy link
Member

Calinou commented Dec 31, 2022

I can confirm this on 4.0.beta b6e0603 (Linux, AMD Radeon RX 6900XT with Mesa RADV). I can't reproduce this after switching to the Forward Plus (clustered) backend, so this is an issue specific to the mobile backend.

The presence of a DirectionalLight3D in the scene doesn't affect this issue (the MRP doesn't have any, it's only a preview in the editor).

Edit: This is actually (probably) not a bug. Decals start flickering after you place more than 7 of them because the mobile backend is limited to 8 decals per mesh resource. The only thing I find unusual is that decals start to break after you have more than 7 of them, rather than 8 of them. I can reproduce that in a blank project too.

In general, for small decals such as bullet holes, I recommend placing a MeshInstance with a PlaneMesh instead. This is faster to render, allows for any shader to be used (including BaseMaterial3D parallax) and has no limitations on the number of "decals" you can render, regardless of your backend (even OpenGL). Rendering errors due to decals flying in the air are unlikely to be noticeable during gameplay given the decals' small size.

cc @BastiaanOlij

@Calinou Calinou changed the title Vulkan: Decals pop away when rotating and moving camera and get stuck Vulkan Mobile: Decals pop away when rotating and moving camera and get stuck Dec 31, 2022
@Calinou Calinou added this to the 4.0 milestone Dec 31, 2022
@Calinou Calinou changed the title Vulkan Mobile: Decals pop away when rotating and moving camera and get stuck Vulkan Mobile: Decals pop away when there are more than 7 decals on a mesh resource (instead of the expected 8) Dec 31, 2022
@elvisish
Copy link
Author

elvisish commented Jan 2, 2023

I can confirm this on 4.0.beta b6e0603 (Linux, AMD Radeon RX 6900XT with Mesa RADV). I can't reproduce this after switching to the Forward Plus (clustered) backend, so this is an issue specific to the mobile backend.

The presence of a DirectionalLight3D in the scene doesn't affect this issue (the MRP doesn't have any, it's only a preview in the editor).

Edit: This is actually (probably) not a bug. Decals start flickering after you place more than 7 of them because the mobile backend is limited to 8 decals per mesh resource. The only thing I find unusual is that decals start to break after you have more than 7 of them, rather than 8 of them. I can reproduce that in a blank project too.

In general, for small decals such as bullet holes, I recommend placing a MeshInstance with a PlaneMesh instead. This is faster to render, allows for any shader to be used (including BaseMaterial3D parallax) and has no limitations on the number of "decals" you can render, regardless of your backend (even OpenGL). Rendering errors due to decals flying in the air are unlikely to be noticeable during gameplay given the decals' small size.

cc @BastiaanOlij

Is it possible to raise the limit of the decals per mesh resource? I used mobile as it seemed like a good renderer for retro graphics that don't need high overhead but can still use Vulkan. I also used decals as it it's annoying when bullet holes are misaligned on surfaces; still, good to know that PlaneMesh is faster to render as I assumed Decals would have been far mroe optimised for this kind of thing.

@Calinou
Copy link
Member

Calinou commented Jan 2, 2023

Is it possible to raise the limit of the decals per mesh resource?

No, it's not technically possible to do so due to how the renderer is architectured.

@elvisish
Copy link
Author

elvisish commented Jan 3, 2023

Is it possible to raise the limit of the decals per mesh resource?

No, it's not technically possible to do so due to how the renderer is architectured.

Could it be re-architectured so it does work on mobile? I can't imagine any reason why mobile couldn't benefit from decals working as well as Forward+? Also, I think there should probably be a warning if Mobile is used with decals.

@Calinou
Copy link
Member

Calinou commented Jan 3, 2023

Could it be re-architectured so it does work on mobile? I can't imagine any reason why mobile couldn't benefit from decals working as well as Forward+?

The Forward Mobile backend uses a single-pass approach to light and decal rendering, but without clustering (as clustering is expensive on its own, and not suited to mobile GPUs). To keep performance high and shader compilation times reasonable, it's not possible to support a high number of lights/decals per mesh with a traditional single-pass forward approach.

Either way, you should be splitting your level into several meshes in a real world project (so that it can benefit from frustum/occlusion culling). You need to find a balance to avoid having too many draw calls though. Usually, you want each room in the level to be its own MeshInstance3D node.

@elvisish
Copy link
Author

elvisish commented Jan 3, 2023

Either way, you should be splitting your level into several meshes in a real world project (so that it can benefit from frustum/occlusion culling). You need to find a balance to avoid having too many draw calls though. Usually, you want each room in the level to be its own MeshInstance3D node.

Impossible, I'm using TBLoader which imports the map as one giant mesh and since Godot has no built-in way of making level geometry, this is currently the best solution (outside of building in Blender, which has other drawbacks). @codecat might be able to explain why TBLoader worked best loading as one big mesh (I believe there was an early attempt at using multiple meshes that was abandoned).

@Calinou
Copy link
Member

Calinou commented Jan 3, 2023

Impossible, I'm using TBLoader which imports the map as one giant mesh

This is something that should be addressed within the add-on. Having the entire level be a single mesh is going to cause performance issues in complex levels – you can see this being an issue in the tps-demo.

It is possible to procedurally perform mesh splitting, but it's difficult to get right: https://github.com/lawnjelly/godot-splerger

@elvisish
Copy link
Author

elvisish commented Jan 3, 2023

Impossible, I'm using TBLoader which imports the map as one giant mesh

This is something that should be addressed within the add-on. Having the entire level be a single mesh is going to cause performance issues in complex levels – you can see this being an issue in the tps-demo.

It is possible to procedurally perform mesh splitting, but it's difficult to get right: https://github.com/lawnjelly/godot-splerger

Qodot (which TBLoader is based on though greatly improved) also loaded as one big mesh, it's a shame decals can't be limited on mobile mode to 8 per surface instead of mesh.

@clayjohn
Copy link
Member

clayjohn commented Jan 3, 2023

I feel like it should be possible to make the limitation a per-surface limitation. @BastiaanOlij would know more. But I think the decal list ends up getting assigned to a surface anyway so there shouldn't be any real benefit to storing it per mesh instead of per surface

@BastiaanOlij
Copy link
Contributor

BastiaanOlij commented Jan 4, 2023

@clayjohn I think it already works per surface but I could be wrong. Not sure that would help in this case as there is still a high chance the surface covers a lot of area.

I'm with @Calinou on this, this is a flaw in the approach of the level loader. Having your whole level be one big mesh on any sizable level will be a huge issue performance wise as nothing can be culled and all geometry is handled.

Also even if we "solve" this limitation, it would mean every fragment drawn for the level mesh would be looping through all decals, so you'd have a huge performance issue.

While there are options to do a version of clustering in fragment shaders (one of my old engines works this way) I'm not sure how well that would perform on mobile hardware.

@elvisish
Copy link
Author

elvisish commented Jan 4, 2023

I'm with @Calinou on this, this is a flaw in the approach of the level loader. Having your whole level be one big mesh on any sizable level will be a huge issue performance wise as nothing can be culled and all geometry is handled.

Cruelty Squad (PC Gamer Game of the Year 2021) was made using Qodot which uses a single mesh per level and had no performance issues, so it does work.

@codecat
Copy link
Contributor

codecat commented Jan 4, 2023

I suppose it depends on the complexity of the level geometry. You could use TBLoader to load parts of the world as separate meshes by just creating different layers in TrenchBroom (each layer has its own node & MeshInstance3D). Segmenting a single large layer could potentially be done automatically, but such a feature would be more of an issue to be discussed in the TBLoader repository instead.

@BastiaanOlij
Copy link
Contributor

Cruelty Squad (PC Gamer Game of the Year 2021) was made using Qodot which uses a single mesh per level and had no performance issues, so it does work.

Not sure if that is a fair comparison in this case, yes it works, it works well, if you keep the lighting within the limits. I'm not even sure if Cruelty Squad uses lighting or if it does it probably just has a single directional light.

@elvisish
Copy link
Author

elvisish commented Jan 4, 2023

it works well, if you keep the lighting within the limits.

Is performance affected by lights? I thought the limit of lights per mesh was raised even before 4.0, or do you mean if shadows/light maps are used?

nothing can be culled and all geometry is handled.

Even though gridmaps are highly optimised, culling/portals can't be used on them either so you'd have a similar problem if trying to cull manually; though it probably does some of this for you internally, wouldn't multiple decals per grid map still be an issue? (I'm not sure as I haven't tried yet)

@BastiaanOlij
Copy link
Contributor

it works well, if you keep the lighting within the limits.

Is performance affected by lights? I thought the limit of lights per mesh was raised even before 4.0, or do you mean if shadows/light maps are used?

Lights are expensive to calculate. In the clustered forward renderer we run a pre-process that allows us to see which fragments (pixels) are effected by which lights, so we only do light calculations for lights that actually impact a fragment. So even if you have 1000 lights, if only 2 light this fragment, you're only doing 2 lighting calculations. Even on a big mesh that is lit by many lights, because we're able to do this filtering on a per fragment basis, the lighting calculations are kept to a minimum.

But this approach does not work well on mobile so on mobile we use a more traditional way where we determine which lights effect a mesh. This means that for every fragment we have to process every light that effects the entire mesh, even if that fragment is only lit by one or two of those lights. It also means transfering that information to the GPU before rendering each mesh, which is limited without having a performance impact.

So it's a double whammy, we're limited to how much information we sent, so that limits the maximum number of lights, and every light we check has an impact on performance, especially on large meshes hit by many lights.

nothing can be culled and all geometry is handled.

Even though gridmaps are highly optimised, culling/portals can't be used on them either so you'd have a similar problem if trying to cull manually; though it probably does some of this for you internally, wouldn't multiple decals per grid map still be an issue? (I'm not sure as I haven't tried yet)

Gridmaps use multimeshes behind the scene. A multimesh is used for each tile. So the same goes here, if a gridmap spans over a large area, it's efficiency starts to suffer as the balance tips from limiting drawcalls, to parsing a lot of geometry that isn't even on screen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

5 participants