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

OCULUS_multiview implementation w/ multisampling support #24048

Closed
wants to merge 0 commits into from

Conversation

snagy
Copy link
Contributor

@snagy snagy commented May 11, 2022

Related issue: #20368

This is a crack at reimplementing multiview for XR devices that support the OCULUS_multiview extension. This extension lets us use multiview along with MSAA.

This is a huge speed improvement for applications that are CPU limited and draw count bound. Even the VR ballshooter example runs at a higher framerate when shooting on the quest 2 with multiview enabled than without.

Unfortunately the extension has one big problem - the multisampled render to texture extension this uses will discard the frame buffer if other texture operations are used during rendering. This is outlined in KhronosGroup/WebGL#2912 . I've added a deferred upload of texture data (such as video frames) to mitigate this problem, by deferring uploads until after rendering of the main scene has finished. This adds a frame of latency to the texture uploads, and is only active if multiview is enabled.

Some apps (mostly ones that render stuff like real time reflections to a texture) won't work with this extension without refactoring when those renders happen, to ensure that they don't interrupt the main frame.

Most experiences will get a free perf gain from this extension, but some will get broken. Because of this, I do not enable multiview by default and instead added a 'multiviewStereo' flag to the renderer properties to enable it. If this flag isn't set, everything should work as before.

@Mugen87
Copy link
Collaborator

Mugen87 commented May 12, 2022

It seems the PR modifies some example files which are unrelated to this change (like BufferGeometryUtils, HTMLMesh etc.). Even if these modifications are correct, it still clearer to not include them in the PR.

@LeviPesin
Copy link
Contributor

Please also remove build and *.d.ts files.

@snagy snagy force-pushed the oculus-multiview branch from 0546536 to 3e49599 Compare June 2, 2022 17:48
@mrdoob mrdoob added this to the r142 milestone Jun 7, 2022
@mrdoob
Copy link
Owner

mrdoob commented Jun 7, 2022

Unfortunately the extension has one big problem - the multisampled render to texture extension this uses will discard the frame buffer if other texture operations are used during rendering. This is outlined in KhronosGroup/WebGL#2912 . I've added a deferred upload of texture data (such as video frames) to mitigate this problem, by deferring uploads until after rendering of the main scene has finished. This adds a frame of latency to the texture uploads, and is only active if multiview is enabled.

Do you know if this is something that will get fixed in the spec somehow?

Ideally this is something that the renderer would just use when available and the developer shouldn't have to learn all these details and have to choose between higher CPU usage or being a frame late...

@snagy
Copy link
Contributor Author

snagy commented Jun 7, 2022

Do you know if this is something that will get fixed in the spec somehow?

Ideally this is something that the renderer would just use when available and the developer shouldn't have to learn all these details and have to choose between higher CPU usage or being a frame late...

I agree completely that I want this to be automatic and error free. From my understanding, fixing it at a spec level isn't trivial - you've basically got to pick your poison between slowdown and shimmering (with automatically handled resolves) or failed rendering (with discard on resolve). AFAICT, both options require changes to how three handles texture uploads to behave optimally.

Because this is such a big win for experiences that can use it, I'm inclined to check it in behind a flag so folks can opt-in and see if the limitations are acceptable for their app. I'll also keep looking at how we can make it easier to use, both on the platform side and three.js, and see if I can find a generalized solution for everyone that doesn't involve the upload delay.

@mrdoob
Copy link
Owner

mrdoob commented Jun 8, 2022

From my understanding, fixing it at a spec level isn't trivial - you've basically got to pick your poison between slowdown and shimmering (with automatically handled resolves) or failed rendering (with discard on resolve).

@rcabanier Do you have any insights on this?

@cabanier
Copy link
Contributor

From my understanding, fixing it at a spec level isn't trivial - you've basically got to pick your poison between slowdown and shimmering (with automatically handled resolves) or failed rendering (with discard on resolve).

@rcabanier Do you have any insights on this?

It is unlikely that this will be fixed in the OpenGL spec. Just as with ext_multisampled_render_to_texture this was done on purpose because storing/loading and binding/unbinding are very costly on mobile hardware.
By putting in this flushing behavior, developers are forced to update their code so they don't run into slowdowns.

That being said, when we switch to the Vulkan backend, we will have more control over loading and storing so we can fix it ourselves.

@cabanier
Copy link
Contributor

Having multisampled multiview as an option will definitely be very helpful. It's hard to have complex immersive scenes on our limited hardware and this feature greatly helps code that is CPU limited.

@mrdoob
Copy link
Owner

mrdoob commented Jun 15, 2022

@cabanier Thanks!

What about this other issue?

Unfortunately the extension has one big problem - the multisampled render to texture extension this uses will discard the frame buffer if other texture operations are used during rendering.

Is it possible to refactor WebXRManager so the old experiences continue working with and without OCULUS_multiview?

If we are not able to implement this without the developer having to know about it I don't think this PR will get merged.

@cabanier
Copy link
Contributor

Unfortunately the extension has one big problem - the multisampled render to texture extension this uses will discard the frame buffer if other texture operations are used during rendering.

Is it possible to refactor WebXRManager so the old experiences continue working with and without OCULUS_multiview?

If we are not able to implement this without the developer having to know about it I don't think this PR will get merged.

Are you proposing that we detect problematic calls that cause flushes and then disable the extension (along with a warning)?

@mrdoob
Copy link
Owner

mrdoob commented Jun 17, 2022

Are you proposing that we detect problematic calls that cause flushes and then disable the extension (along with a warning)?

No no. I was hoping @snagy could clarify what exactly breaks when using OCULUS_multiview and what changes we need to do in the library and/or examples so using OCULUS_multiview doesn't break anything.

@snagy
Copy link
Contributor Author

snagy commented Jun 17, 2022

Whenever you do a texture upload or bind another framebuffer or anything else in the list in the link in the first post, it drops the framebuffer and nothing else draws to that buffer for the rest of the frame.

The workaround in this PR (deferring texture uploads until the frame is complete) is the best thing I can come up with to fix most experiences without large changes to three. For a lot of apps (like the ball shooter examples), that's fine. They get a huge perf boost with no visual cost.

The biggest problem with this workaround is that skeletal meshes upload their bone textures with TexImage, which is one of the prohibited operations. With the workaround, that means that all bone animation lags by one frame. If this PR lands, the next thing I would do is try to move the skinnedmesh processing and upload before the multiview scene begins rendering, so they don't interrupt the render but also aren't delayed.

Beyond that, other textures could potentially be uploaded the same way - before the multiview scene loop begins. Stuff like movie textures are one frame delayed with the workaround, which could be fixed with some work. We have movies in our app but the frame delay on a movie is less consequential (for us) than the skinned mesh delay.

The trickiest usage to fix will be applications that switch off to render another view (such as a mirror reflection) in the middle of the scene. We would also have to move this to the start of the frame so it doesn't interrupt rendering, and I'm not sure we can easily guarantee that without changing a lot of dev expectations on how three works. It's possible to fix this on a per-app basis as a dev, though - our app includes a camera that renders its own view of the scene, and we were able to render that before the multiview frame by just architecting our code properly.

I'm skeptical that we could get it to a universal, always-on implementation until the browser and drivers allow more control over when the multisample resolve happens. (AFAIK this is possible once we're using Vulkan as our base API in the browser, instead of GLES).

@cabanier
Copy link
Contributor

cabanier commented Jun 18, 2022

For good performance on mobile GPUs three.js should be doing these optimizations anyway; it's just more obvious if this extension is turned on (which is the reason they put this limitation in).
Ideally three would move to a multipass design where texture/mesh uploads happen first, followed by reflections and then the final render.

@mrdoob mrdoob modified the milestones: r142, r143 Jun 29, 2022
@mrdoob mrdoob modified the milestones: r143, r144 Jul 28, 2022
@mrdoob mrdoob modified the milestones: r144, r145 Aug 31, 2022
@cabanier
Copy link
Contributor

@snagy it looks like your patch still has conflicts

@snagy
Copy link
Contributor Author

snagy commented Sep 21, 2022

@snagy it looks like your patch still has conflicts

I'm happy you didn't come by when I accidentally pushed 366 pre-existing commits into this branch. The force push was just patching up that mistake.

@mrdoob mrdoob modified the milestones: r145, r146 Sep 29, 2022
@marcofugaro
Copy link
Contributor

Hey, this optimization is really useful, would be cool to have it available.

@mrdoob mrdoob modified the milestones: r146, r147 Oct 27, 2022
@HexaField
Copy link
Contributor

This currently seems to not account for the skybox. It renders as a unit box centered on the camera, ie each face 0.5 units from the camera.

@snagy
Copy link
Contributor Author

snagy commented Nov 28, 2022

Just to be clear, by skybox do you mean the scene.background?

@HexaField
Copy link
Contributor

Just to be clear, by skybox do you mean the scene.background?

Sorry, yes.

@mrdoob mrdoob modified the milestones: r147, r148 Nov 30, 2022
@mrdoob mrdoob modified the milestones: r148, r149 Dec 22, 2022
@snagy
Copy link
Contributor Author

snagy commented Jan 11, 2023

This currently seems to not account for the skybox. It renders as a unit box centered on the camera, ie each face 0.5 units from the camera.

I finally got around to testing this and didn't see this problem - I made a scene with a cube map background and it rendered properly with and without multiview. It looks like that code change a bit, (in pr #24805 ) so it might have been fixed there.

@HexaField
Copy link
Contributor

HexaField commented Jan 17, 2023

This currently seems to not account for the skybox. It renders as a unit box centered on the camera, ie each face 0.5 units from the camera.

I finally got around to testing this and didn't see this problem - I made a scene with a cube map background and it rendered properly with and without multiview. It looks like that code change a bit, (in pr #24805 ) so it might have been fixed there.

After some more testing, we are only seeing this in the Meta Quest Pro. We have updated our fork of this PR with the latest changes and threejs version, and the issue remains.

Update: We have modified L70 in WebGLBackground.js to have a size of 10000 for all parameters. This seems to be good enough for now.

@snagy
Copy link
Contributor Author

snagy commented Jan 18, 2023

Update: We have modified L70 in WebGLBackground.js to have a size of 10000 for all parameters. This seems to be good enough for now.

I poked at this and thought about it a bit - I validated it was happening on my Quest Pro and then went back and saw the same behavior on my Q2. It's incredibly uncomfortable, so I'm not sure if somehow I didn't notice it originally or if something else was happening. I'll test this a few more times to see if there's more going on there.

Anyway, I think the behavior on the quest pro is 'correct' - it was creating a 1 meter box and then moving it to match the camera origin each frame. Unfortunately for multiview, it's moving it to center on the spot between the cameras and then the multiview cameras still apply their view offsets when the box gets rendered, causing it to 'properly' offset per eye as though you've got a 1 meter box around your head.

The two solutions are either a) special case how the projection happens so the background projection isn't affected by camera position or b) just make the box really big. (b) is the simpler option so I've updated this PR to include that.

@mrdoob mrdoob modified the milestones: r149, r150 Jan 26, 2023
@mrdoob mrdoob modified the milestones: r150, r151 Feb 23, 2023
@mrdoob mrdoob modified the milestones: r151, r152 Mar 30, 2023
@mrdoob mrdoob modified the milestones: r152, r153 Apr 27, 2023
@snagy snagy closed this May 3, 2023
@snagy snagy force-pushed the oculus-multiview branch from 7a3bb2c to 1d91a4a Compare May 3, 2023 23:43
@mrdoob mrdoob removed this from the r153 milestone May 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants