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

Use a spec constant to control whether the MultiMesh branch is used in the vertex shader. #94289

Merged

Conversation

clayjohn
Copy link
Member

Fixes: #89985

This works around a driver bug on the Quest3 and slightly improves performance on all mobile devices at the cost of increased pipeline count.

After extensive debugging we narrowed this issue down specifically to the is_multimesh branch. We are 99% certain that there is a driver bug on the Quest3 hardware. For whatever reason, the presence of this branch causes the flickering. My current theory is that the hardware is running the branch and masking the result (which shouldn't happen with a uniform branch) and it is failing to mask the result properly so data from that branch is leaking out. Since there is no transform buffer with regular meshes, we end up with an invalid model matrix which causes the meshes to jump around. Also note, that MultiMeshes don't exhibit the flickering.

We found that manually removing the branch fixed the issue.

We have opted to use specialization constants to fix the issue for 4.3 as they have the same effect as removing the branch entirely. Luckily, this change improves performance on all platforms and shouldn't have a significant impact on pipeline compilation. Ironically, we already planned on moving this branch to a specialization constant, but we were waiting for the ubershader PR to be merged first. But since it isn't coming until 4.4, we need to do this separately.

One concern, the Ubershader uses a uniform buffer instead of specialization constants. If we are not careful, it may reintroduce this bug, but only for the Ubershader variants (which would make the flickering happen, but only rarely). CC @DarioSamo I think we will need to ensure that non multimesh specialization is compiled at load time. The multimesh specialization can fall back on the Ubershader.

This PR was a joint effort between Bastiaan, David, Dario, Gordo, and I. Together we brainstormed and arrived at this change by process of elimimination. For posterity, here is an (incomplete) list of other problems we checked before arriving at this workaround:

  1. Failure to synchronize swap chain
  2. Failure to set subpass dependencies
  3. Subpass resolve failure
  4. Failing to synchronize after writing instance buffer
  5. Null pointers in the instance buffer
  6. Subpass driver bug
  7. Failing to synchronize between render passes

…n the vertex shader.

This works around a bug on the Quest3 and slightly improves performance on all mobile devices at the cost of increased pipeline count.
@DarioSamo
Copy link
Contributor

DarioSamo commented Jul 13, 2024

One concern, the Ubershader uses a uniform buffer instead of specialization constants. If we are not careful, it may reintroduce this bug, but only for the Ubershader variants (which would make the flickering happen, but only rarely). CC @DarioSamo I think we will need to ensure that non multimesh specialization is compiled at load time. The multimesh specialization can fall back on the Ubershader.

That's something I was thinking about if this was indeed necessary. It does sound a bit random that we have to guess what particular branch will fail to work in this particular hardware until the issue is resolved.

One workaround could be we just introduce a particular code path that if a mesh uses multimesh, we demand for the specialization to be compiled if it's encountered while rendering but only on the hardware where we know the bug exists. We'd add a stutter, but it'd only be for multimesh and perhaps until the issue is resolved that's enough.

@@ -178,8 +182,6 @@ void main() {
color_interp = color_attrib;
#endif

bool is_multimesh = bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH);

Copy link
Contributor

@MarianoGnu MarianoGnu Jul 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is changing the way is_multimesh is detected in the scene_forward_movine.glsl shader, but non mobile shaders will continue using the old way. Is there a reason to not unify criterias in different shaders?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we will be changing how multimesh are handled in both shaders in 4.4. But before we do that, we need to merge #90400 otherwise we will make the shader compilation stutter much worse.

This PR is a band aid fix for 4.3. It only targets Mobile since the Quest3 is the only device with this driver bug and the cost of adding a new spec_constant in the Forward+ shader is much higher. The way the Mobile renderer works right now, this change won't increase shader compilation stutter, so it is safe to make. In the Forward+ renderer this change could increase shader compilation stutter (its already a much bigger problem there) and since the Forward+ renderer can't be used on the Quest3, it doesn't really provide a benefit.

Copy link
Contributor

@RevoluPowered RevoluPowered left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been tested with The Mirror and resolves the flickering problem with the Quest 3.

I tested this using shaders, the mobile renderer and a large scene with a large terrain.

I was present when the fix was being developed and this was the best way forward for 4.3.

My performance felt good in the standalone headset, which is great.

Copy link
Contributor

@dsnopek dsnopek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can also confirm that this fixes the fickering issue on Quest 3 in my testing!

@akien-mga akien-mga merged commit c5e5fa3 into godotengine:master Jul 17, 2024
18 checks passed
@akien-mga
Copy link
Member

Thanks!

@crowgames
Copy link

Thanks!!!

@clayjohn clayjohn deleted the MOBILE-multimesh-spec-constant branch July 17, 2024 18:16
RevoluPowered added a commit to the-mirror-gdp/the-mirror that referenced this pull request Jul 25, 2024
* Android Support for The Mirror for Meta Quest 2/3

What does this do:
- Major engine upgrade to a much newer version of Godot Engine, with the fixes we helped with on the meta quest 3. (https://github.com/the-mirror-gdp/godot/tree/gordon/rebase-rc-2 to be merged)
- Adds performance advantages for quest2/quest3 and low end hardware.
- In theory fixes the intel crashing issues thanks to us removing our blur shader for the UI. (it was a huge performance hit)
- Allows the Game UI to be used from within VR, renders all existing UI. (Doesn't allow the build menu but this was out of scope for this work)
- Ensures the player is not visible for themselves on the local client, this will ensure no weird clipping happens.
- The mirror can run in standalone mode on the quest 2 and quest 3.
- Android doesn't flicker anymore. (Thanks to the Godot team for their amazing help ❤️) Use a spec constant to control whether the MultiMesh branch is used in the vertex shader. godotengine/godot#94289
- GameUI had to be moved to GameUI.instance as GameUI.instance is set based on if you are using VR or if you are using the Desktop UI, they are very different. GameUI.instance is useful because it ensures that you point at the correct UI and that you don't need to delete/readd the game UI, this was a major refactor and hugely painful to get working, there may be issues we do not know about without wider testing.
- Forces glow and various graphical settings off when using VR, this is because it must be disabled officially any post processing except MSAA should be disabled for VR, baring foveation and some settings under OpenXR.
- Removes the GameUI autoload and replaces it with the GameUI singleton using GameUI.instance.

This was a combination of the GodotEngine team's rendering fixes. Special thanks to them.
@kuruk-mm
Copy link
Contributor

kuruk-mm commented Aug 30, 2024

can this changes be cherry-picked for 4.2.3? @akien-mga

@akien-mga
Copy link
Member

I'll check with Clay what he thinks.

@akien-mga akien-mga added the cherrypick:4.2 Considered for cherry-picking into a future 4.2.x release label Aug 30, 2024
kuruk-mm pushed a commit to dclexplorer/godot that referenced this pull request Aug 30, 2024
…pec-constant

Use a spec constant to control whether the MultiMesh branch is used in the vertex shader.
@clayjohn
Copy link
Member Author

Yes, this can be cherrypicked for 4.2. I think it should be a clean cherrypick

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug cherrypick:4.2 Considered for cherry-picking into a future 4.2.x release performance platform:android topic:rendering topic:3d
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Vulkan Mobile: Mesh Flickering in VR on Quest 3
9 participants