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

Fix rendering issue with depth in WebXR #88787

Merged
merged 1 commit into from
Mar 5, 2024

Conversation

dsnopek
Copy link
Contributor

@dsnopek dsnopek commented Feb 24, 2024

Currently on master, in WebXR there is a rendering issue with depth where it draws some triangles in front, that really should be behind.

Here's a screenshot showing the problem (most obvious on the fingers):

ab052a9ff5c4c335aab55d052fb06fa6

And here's a screenshot with this PR:

8ced2edeec825a7e9af9a8a7f099fdba

It looks like this issue was introduced in PR #87386

@clayjohn
Copy link
Member

It sounds like the depth just wasn't being written as we suspected. But the fix seems odd to me. The result is that we will define a fragment out value even when rendering to depth which could have a negative performance impact on some devices.

I'll take a deeper look on Monday and see how those defines are set

@dsnopek
Copy link
Contributor Author

dsnopek commented Feb 28, 2024

I've been looking at this some more. It seems like the glDrawBuffers(0, nullptr) before doing the depth-only pass should make it so we don't need to have the fragment color out.

However, I wonder if there are some differences here between OpenGL and GLES? Is it possible that some GLES implementations require having a fragment color out if the framebuffer has a color attachment?

Looking at the scene.glsl for GLES3 in Godot 3, it appears to always be declaring the fragment color out, even when RENDER_DEPTH is defined (which I think is the equivalent to MODE_RENDER_DEPTH, but I'm less familiar with the Godot 3 renderer). Perhaps Godot 3 is doing that for a reason?

@dsnopek
Copy link
Contributor Author

dsnopek commented Mar 2, 2024

I had the idea that maybe switching all the glDrawBuffers(0, nullptr) to this might work:

{
  GLuint db = GL_NONE;
  glDrawBuffers(1, &db);
}

Since I saw some WebGL code like gl.drawBuffers([ gl.NONE ]) (which is the Javascript equivalent) when trying to Google this problem.

Unfortunately, it didn't work. :-(

@clayjohn Do you have any other ideas? So far this PR is the only thing that has worked for me to fix this issue.

@clayjohn
Copy link
Member

clayjohn commented Mar 4, 2024

Looking more closely, I can see that the only different in behaviour is in the cases where MODE_RENDER_DEPTH is defined.

The more I look into this, the more it doesn't make sense. Here is the WebGL 2.0 spec talking about the differences from GL ES 3.0:

5.4 Draw Buffers
The value of the MAX_COLOR_ATTACHMENTS parameter must be equal to that of the MAX_DRAW_BUFFERS parameter.

There is no use case for these parameters being different.
If an ESSL1 fragment shader writes to neither gl_FragColor nor gl_FragData, the values of the fragment colors following shader execution are untouched. If corresponding output variables are not defined in an ESSL3 fragment shader, the values of the fragment colors following shader execution are untouched.

All user-defined output variables default to zero if they are not written during a shader execution.

For optimal performance, an output array should not include any elements that are not accessed.

If the values written by the fragment shader do not match the format(s) of the corresponding color buffer(s) (for example, the output variable is an integer, but the corresponding color buffer has a floating-point format, or vice versa), and the color buffer states are not set to NONE by DrawBuffers, draws generate an INVALID_OPERATION error; if the color buffer states are set to NONE by DrawBuffers, they remain untouched.

If any draw buffer with an attachment does not have a defined fragment shader output, draws generate INVALID_OPERATION, unless all 4 channels of colorMask are set to false.

The last comment is particularly interesting here. It implies that we can write only depth to a framebuffer with a bound color attachment , but we need to have a proper colorMask.

In Godot we write 0 instead of false

glColorMask(0, 0, 0, 0);

Ultimately, I can't see anything that would cause this issue. So let's go ahead with this PR. It reintroduces the state we were in prior to #87386 with respect to defining frag_color. Plus, frag_color is never written and glDrawBuffers should ensure that the driver knows it won't be written.

Copy link
Member

@clayjohn clayjohn left a comment

Choose a reason for hiding this comment

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

Let's go ahead with this. We can remove the check for MODE_RENDER_DEPTH since we don't actually care about it if we are always using some color output

drivers/gles3/shaders/scene.glsl Outdated Show resolved Hide resolved
drivers/gles3/shaders/scene.glsl Outdated Show resolved Hide resolved
drivers/gles3/shaders/scene.glsl Outdated Show resolved Hide resolved
@dsnopek
Copy link
Contributor Author

dsnopek commented Mar 4, 2024

@clayjohn Thanks! I've made your suggested changes in my latest push :-)

@akien-mga akien-mga merged commit 8680772 into godotengine:master Mar 5, 2024
16 checks passed
@akien-mga
Copy link
Member

Thanks!

@dsnopek dsnopek deleted the webxr-depth-draw-fix branch July 22, 2024 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants