Repository navigation
Support WebXR DirectRenderPipeline on VisionOS - #35028
AdaRoseCannon wants to merge 2 commits into
Conversation
…et from the projection layer, using its logical viewport, and supplying single-sample foveated depth for reprojection
|
cc @cabanier (tested on quest and visionOS) |
📦 Bundle sizeFull ESM build including core, minified and gzipped.
🌳 Bundle size after tree-shakingMinimal build including a renderer, camera, empty scene, and dependencies.
|
cabanier
left a comment
There was a problem hiding this comment.
@AdaRoseCannon , your change disables MSAA in WebXR.
Are you planning on bringing it back in a follow up PR?
| viewDescriptors: [], | ||
| viewports: [] | ||
| viewports: [], | ||
| depthTextures: [], |
There was a problem hiding this comment.
The spec says that the same texture needs to be returned for left and right eye. Treat the depth texture like colorTexture
There was a problem hiding this comment.
Should we pass depthStencilFormat here? The current WebXR WebGPU spec says omitting it means no depth texture is allocated. That leaves ignoreDepthValues true, so the new supplied-depth path depends on the browser implicitly allocating depth.
Alternatively, we can update the spec to leave it up to the user agent
There was a problem hiding this comment.
I'd rather follow the spec at this point, so i'll fix it :)
There was a problem hiding this comment.
if we put it here will Quest then supply a depth buffer? I think it will ignore this and it won't, right?
There was a problem hiding this comment.
if you put it here, quest will create a depth buffer. Use usesDepthValues on the gpubinding. If it's true, ask for depth textures
| if ( supplyDepth === true && gpuSubImage.depthStencilTexture ) { | ||
|
|
||
| viewData.depthTextures.push( gpuSubImage.depthStencilTexture ); | ||
| viewData.depthViewDescriptors.push( gpuSubImage.getViewDescriptor() ); |
There was a problem hiding this comment.
getViewDescriptor() returns a descriptor usable for both color and depth. Can we reuse viewDescriptors and remove depthViewDescriptors and the second call?
| xrRenderTarget.dispose(); | ||
|
|
||
| // The external texture can be registered before the render target is initialized. | ||
| for ( const texture of xrRenderTarget.textures ) { |
There was a problem hiding this comment.
Can we also clear the external depth wrapper’s backend record here?
Its registration bypasses Textures.updateTexture(), so normal texture cleanup does not remove it, and this loop only handles color textures. We should release the cached depth references without destroying the browser-owned GPU texture
| } | ||
|
|
||
| // The pass viewport comes from the render target viewport. Use the logical sub-image | ||
| // viewport: on visionOS it is larger than the foveated texture and the runtime maps it. |
There was a problem hiding this comment.
Could we describe this as “using runtime-provided single-sample depth for reprojection”? The logical viewport explanation is useful, but please clarify that foveation is handled by the runtime and this PR consumes its viewport and depth texture.
The issue is because the same sample count is needed for all the attachments in the render pass and the WebXR provided depth and colour textures have I wasn't planning a follow up PR. if you have an idea for a solution it would be very welcome |
I think there's a way to do it but we'll have to teach three.js on how to resolve depth. We can do that after this lands. |
…, request depth unless usesDepthValues is false, release the depth record on session end
Oh nice, thanks! |
|
Your change looks good! There could be instances (ie color mapping) where depth is lost. |
I tested to see what happens if depth isn't provided, it doesn't break everything but the quality of the reprojection drops significantly. This usually looks like warping around objects close to the viewer. |
|
ok, so a warning should suffice. |
|
Just for my understanding: Without |
| if ( xrRenderTarget.depthTexture !== null ) { | ||
|
|
||
| xrRenderTarget.depthTexture.image.width = layerWidth; | ||
| xrRenderTarget.depthTexture.image.height = layerHeight; |
There was a problem hiding this comment.
I think this resize is redundant. Textures.updateRenderTarget() does that for you.
| const depthFormat = this._renderer.stencil ? DepthStencilFormat : DepthFormat; | ||
| const depthType = this._renderer.stencil ? UnsignedInt248Type : UnsignedIntType; | ||
| depthTexture = new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat, 2 ); | ||
| depthTexture.isArrayTexture = true; |
There was a problem hiding this comment.
This line is redundant as well. Textures.updateRenderTarget() should set that flag automatically.
That's correct.
Yes that's correct. A single write without the depth buffer causes some distortion but doesn't make an experience unusable. Unfortunately the two writes cause foveation to get applied twice which results in pinched appearance which is unusable. We've had some discussions recently in the Immersive Web WG about ways to support multiple render passes with foveation but it still needs further discussion with the WebGPU WG before we can decide on a solution. |
FYI @mwyrzykowski and I are working on extending the WebGPU for WebXR spec to clean this up: immersive-web/webxr-webgpu-binding#41 |
Support WebXR DirectRenderPipeline on VisionOS by sizing the XR render target from the projection layer, using its logical viewport, and supplying single-sample foveated depth for reprojection
Description
VisionOS can't expose the dimensions of the projection layer before the first frame, so the XR render target is resized when the size is available
VisionOS requires the depth buffer for reprojection so when the depth buffer is not ignored the WebXR provided depth texture is used
On visionOS the sub-image viewport is the logical (pre-foveation) size, which is larger than the projection layer texture size so that is used for rendering