Skip to content

XRManager: Fix layer color correction.#31124

Merged
Mugen87 merged 1 commit into
mrdoob:devfrom
cabanier:xr_multisampling
May 18, 2025
Merged

XRManager: Fix layer color correction.#31124
Mugen87 merged 1 commit into
mrdoob:devfrom
cabanier:xr_multisampling

Conversation

@cabanier

Copy link
Copy Markdown
Contributor

Addresses comment from @mrdoob

@Mugen87 , this "fixes" the problem but still has bad performance. The issue seems to be coming from the single _frameBufferTarget in the renderer, as well as its single _quad.
Because of the single target, textures and shaders get reallocated constantly. Because of the single fixed size quad, the layers get rendered to a texture that is too big in the first pass.
Maybe we can give each layer its own renderer or maybe we can fix the renderer so it correctly caches/reallocates the resources for the second pass.

@github-actions

Copy link
Copy Markdown

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 337.26
78.64
337.26
78.64
+0 B
+0 B
WebGPU 550.17
152.5
550.12
152.5
-49 B
-2 B
WebGPU Nodes 549.52
152.34
549.47
152.34
-49 B
-1 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 468.12
113.14
468.12
113.14
+0 B
+0 B
WebGPU 625.54
169.28
625.49
169.28
-49 B
+0 B
WebGPU Nodes 580.39
158.59
580.34
158.59
-49 B
-2 B

@Mugen87 Mugen87 changed the title fix layer colorcorrection XRManager: Fix layer color correction. May 18, 2025
@Mugen87 Mugen87 added this to the r177 milestone May 18, 2025
@Mugen87 Mugen87 merged commit 5223683 into mrdoob:dev May 18, 2025
12 checks passed
@Mugen87

Mugen87 commented May 18, 2025

Copy link
Copy Markdown
Collaborator

Because of the single target, textures and shaders get reallocated constantly.

What exactly triggers the reallocation? I initially thought it's the different resolution of the layers since a resize triggers a dispose of the render target. But I didn't see in the code that the renderer is sized to the layer's render target resolution. Shouldn't there something like below be placed in renderLayers()?

renderer.setPixelRatio( 1 );
renderer.setSize( layer.renderTarget.width, layer.renderTarget.height, false );

This entire situation with XR layers is a bit special since normally the output pass is one of the very last bits of a rendering pipeline that processes the entire image. That is unfortunately not true here. Ideally we could compose the entire XR image and then run the output pass but that doesn't work.

As you mentioned there are multiple solutions for this problem:

  • User a separate renderer per layer. Not sure if this works and it is honestly not by preferred approach.
  • Manage an own framebuffer target per layer.
  • Make the output pass inline. I know this topic has been discussed before but it is a very tempting solution for this use case.

@cabanier

Copy link
Copy Markdown
Contributor Author

Because of the single target, textures and shaders get reallocated constantly.

What exactly triggers the reallocation?

_getFrameBufferTarget has a setSize. This disposes the texture so it needs to get reallocated.

Shouldn't there something like below be placed in renderLayers()?

renderer.setPixelRatio( 1 );
renderer.setSize( layer.renderTarget.width, layer.renderTarget.height, false );

Yes, I tried doing this last week but ran into more problems.

This entire situation with XR layers is a bit special since normally the output pass is one of the very last bits of a rendering pipeline that processes the entire image. That is unfortunately not true here. Ideally we could compose the entire XR image and then run the output pass but that doesn't work.

As you mentioned there are multiple solutions for this problem:

  • User a separate renderer per layer. Not sure if this works and it is honestly not by preferred approach.

Yeah, I assumed that was a non-starter

  • Manage an own framebuffer target per layer.

I also tried this and got a bit further. One issue I ran into was that the multiview logic gets triggered. It likely points to an error in that codepath. I'll look more into this.

  • Make the output pass inline. I know this topic has been discussed before but it is a very tempting solution for this use case.

It requires quite a bit of changes on the Quest browser side but I got most of those done already.
The reason I put it on hold is because WebXR on regular Chrome and on Safari don't support the extension so it would only fix the path for Quest devices.

@donmccurdy

donmccurdy commented May 21, 2025

Copy link
Copy Markdown
Collaborator

The reason I put it on hold is because WebXR on regular Chrome and on Safari don't support the extension ...

It's not obvious to me that any extension or browser changes would be needed to take the image formation code (exposure, tone mapping, conversion to output color space) and relocate it from a separate pass into the material fragment shaders. Possibly I've misunderstood something?

RuthySheffi pushed a commit to RuthySheffi/three.js that referenced this pull request Jun 5, 2025
RuthySheffi pushed a commit to RuthySheffi/three.js that referenced this pull request Jun 5, 2025
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.

3 participants