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

Add MSAA support for WebXR #84686

Merged
merged 1 commit into from Dec 11, 2023
Merged

Add MSAA support for WebXR #84686

merged 1 commit into from Dec 11, 2023

Conversation

dsnopek
Copy link
Contributor

@dsnopek dsnopek commented Nov 9, 2023

This builds on top of PR #83976

It works in my testing on the Meta Quest 2.

Due to one of the weirder parts of WebXR, we need to re-attach the color and depth texture to any FBO using them on every frame. Previously, this was being done via WebXRInterfaceJS::pre_draw_viewport() by updating the render target's FBO. However, that won't work with MSAA because it's not using the render target's FBO, but instead one created by RenderSceneBuffersGLES3, so I had to add a special flag to render targets telling RenderSceneBuffersGLES3 to reattach the textures. This is a little hacky, but I can't think of a better way to do it.

Keeping this as a draft until PR #83976 has been merged

UPDATE: It has been merged! This is no longer a draft :-)

Production edit: closes godotengine/godot-roadmap#72

@dsnopek dsnopek added this to the 4.x milestone Nov 9, 2023
@dsnopek dsnopek requested review from a team as code owners November 9, 2023 23:20
@dsnopek dsnopek marked this pull request as draft November 9, 2023 23:20
@dsnopek dsnopek force-pushed the webxr-msaa branch 3 times, most recently from 63dd028 to ee9aed3 Compare November 11, 2023 15:49
@dsnopek dsnopek self-assigned this Nov 14, 2023
@dsnopek dsnopek modified the milestones: 4.x, 4.3 Nov 14, 2023
@dsnopek dsnopek force-pushed the webxr-msaa branch 3 times, most recently from 9f1dd94 to 1e37913 Compare December 7, 2023 13:45
@dsnopek dsnopek marked this pull request as ready for review December 7, 2023 13:46
@dsnopek dsnopek requested a review from a team as a code owner December 7, 2023 13:46
@dsnopek
Copy link
Contributor Author

dsnopek commented Dec 7, 2023

I just rebased now that the main OpenGL MSAA PR (#83976) has been merged. This should be ready for review!

@@ -461,21 +461,10 @@ bool WebXRInterfaceJS::pre_draw_viewport(RID p_render_target) {
// See: https://immersive-web.github.io/layers/#xropaquetextures
//
// This is why we're doing this sort of silly check: if the color and depth
// textures are the same this frame as last frame, we need to attach them
// textures are the same this frame as last frame, we need to reattach them
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you ever figure out why this is needed? Sounds more like we're forgetting to update the framebuffer somewhere or that it gets updated by code that forgets about the override logic. Seems weird this would only be needed for webXR

Copy link
Contributor Author

@dsnopek dsnopek Dec 11, 2023

Choose a reason for hiding this comment

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

Did you ever figure out why this is needed?

Yep, it's what I'm attempting to explain in the comment, but apparently not very well. :-)

The short version:

Every frame, we need to re-attach the textures we get from WebXR to the framebuffer even if (or, actually, "especially if") they have the same OpenGL id.

The long version:

The WebXR spec invents this concept that doesn't otherwise exist in WebGL called "opaque textures", which is what the link in my comment is about: https://immersive-web.github.io/layers/#xropaquetextures

The goal of opaque textures is to hide the swapchain from the developer. Personally, I think this is a bad idea, because we're really doing a lot of work to hide something that's not really that complex. But it is what is. :-)

To attempt to explain briefly: every frame, we ask WebXR for the texture to render to, and every frame it passes us the exact same texture object. Note: these are objects (not ids) in WebGL. Underneath, each WebGL texture object actually refers to an OpenGL texture ID from the real OpenGL driver, and what's happening is that it's changing that underlying texture id to the next texture in the swapchain, but this is completely hidden from the WebGL-side, where all we see is the exact same texture object. However, since it's actually a different texture underneath, we need to reattach it to the FBO, because otherwise we'd be trying to render to the previous texture in the swapchain.

Where it gets properly is confusing is Emscripten mapping from WebGL to the OpenGL API. Emscripten assigns its own id numbers to the texture objects (which are not related at all to the ids from the real OpenGL driver underneath). It does this by simply setting an id property on the texture object (ex texture.id = 123) because you can just assign arbitrary properties on Javascript objects.

So, from C++, every frame, we're asking WebXR for the texture to render to, and it's just giving us the same id number every frame (because it's the exact same WebGL texture object), but we need to reattach that texture to the FBO anyway, because underneath-underneath it's actually referring to the next texture in the swapchain. This looks silly from C++, because it's the same texture id number every frame, but we need to do it anyway.

(Unfortunately, this also conflicts with any caching that we're doing in Godot, because we make the assumption that each texture id is a unique, individual texture, so that if we get the same texture id, we can just reuse a previously created FBO. However, in this case, because the texture id could really be a different texture underneath, we need to re-attach it anyway.)

Hopefully, that makes sense? I can try and update the comment to explain the situation a little better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm. I spent some time thinking about the comment in the code, and I'm actually not sure I can make it much clearer without making it 3x as long or drawing a picture. :-)

I am going to simplify the logic by just always re-attaching the texture, rather than the sort of confusing check where I'm attempting to account for "what if the browser did actually give us a different object for every texture in the swapchain" which no browsers actually do. That will maybe make the code less confusing overall.

But please let me know if you have any suggestions to make the explanation clearer.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nah all good, it's just so alien and dumb that the penny didn't drop when I read it. It goes against the grain of what a framebuffer object is meant to do (maintain a fixed state you can enable), you're not supposed to update it every frame and on some drivers that would even be fairly costly.

This is why in Vulkan we cache framebuffer objects when go loop through a swapchain like this.

But if that is the direction they went with in WebXR, that even when you have the same ID for an object, underlying you have a changing ID, and thus you need to update your frame buffer, good on them....... ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nah all good, it's just so alien and dumb that the penny didn't drop when I read it. It goes against the grain of what a framebuffer object is meant to do (maintain a fixed state you can enable), you're not supposed to update it every frame and on some drivers that would even be fairly costly.

Yep, I agree, it's pretty dumb.

I'm hoping that one day the standard is amended to not do this. It would actually be pretty easy to do in a backwards compatible way: since we're already required to re-attach the textures every frame, if WebXR started giving us a different texture object for each texture in the swapchain (rather than the same one over and over), existing code wouldn't need to change at all. And if we could detect this new behavior (maybe a flag could be set somewhere?), then we could start caching FBO's and avoid re-attaching the textures every frame, probably for a performance gain.

But we'll see - web standards change very slowly :-)

Copy link
Contributor

@BastiaanOlij BastiaanOlij left a comment

Choose a reason for hiding this comment

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

I think this is good as is, couple of questions that are mostly just nitpicking

@dsnopek
Copy link
Contributor Author

dsnopek commented Dec 11, 2023

@BastiaanOlij Thanks for the review! I've addressed your comments in my latest push

@akien-mga akien-mga merged commit ae7c533 into godotengine:master Dec 11, 2023
15 checks passed
@akien-mga
Copy link
Member

Thanks!

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.

None yet

3 participants