three.js
three.js copied to clipboard
WebGPURenderer's Matrix4 side-effects break tree-shaking
WebGPURenderer has side-effects that directly modify THREE.Matrix4 that were introduced in https://github.com/mrdoob/three.js/pull/20283. This is a bit problematic since those side-effects can't be tree-shaken, and they will bundle alongside threejs in client/server bundles (these can be code-split to be imported from multiple pages/bundles). This can break apps that utilize both WebGLRenderer and WebGPURenderer (like a blog or somewhere you'd have a lot of demos).
I discovered this by importing a flatbundle of three/examples/jsm (from #23368) but you can reproduce by importing the renderer individually:
import { WebGPURenderer } from 'three/examples/jsm/renderers/webgpu/WebGPURenderer'

I've outlined an alternative approach that would also solve the side-effects issue: https://github.com/mrdoob/three.js/issues/20276#issuecomment-687800862
Looking at https://github.com/mrdoob/three.js/blob/dev/examples/jsm/renderers/webgpu/WebGPURenderer.js#L251-L252, I think the way to solve this (including https://github.com/mrdoob/three.js/pull/24383) would to do the following:
- Add
Camera.ndcProjectionMatrixproperty, - Add a parameter
ndc = true/falsetoFrustum.setFromProjectionMatrixmethod.
Looking on these lines again, maybe we even can move NDC-related changes entirely into 2 without doing 1...
What happens if WebGPU camera matrices are used in WebGL? I'm not sure I understand the relationship between the back-end's clipping space and the resulting projection matrices.
What happens if WebGPU camera matrices are used in WebGL?
Near-plane clipping will occur at the wrong distance.
Regarding https://github.com/mrdoob/three.js/issues/25823 this issue is not only problematic for tree-shaking, there are also side effects if an app wants to switch from WebGLRenderer to WebGPURenderer and vice versa.
A global like like suggested in https://github.com/mrdoob/three.js/issues/20276#issuecomment-687800862 would support this use case but not when both renderers are used at the same time (e.g. when rendering to different canvases).
I think the approach described in https://github.com/mrdoob/three.js/issues/23372#issuecomment-1193097640 is the best. Maybe we can try it?
We really need to update Camera.updateProjectionMatrix() out of rendering? Couldn't this be moved inside Renderer.render( scene, camera ) ? This could even simplify the examples...
@sunag Do you mean recomputing projectionMatrix every render?
@sunag Do you mean recomputing projectionMatrix every render?
I think we can have a check to see if there is a need to update, something like const needsUpdateProjectionMatrix = ....
But my biggest concern would not be just simplifying, but as we have the strategy of preserving the API the use of Camera.updateProjectionMatrix() would be incompatible without adding a property (static or not) that we could change between the backend of WebGPU and WebGL because of the use of Matrix4.makePerspective(). This issue could be solved easily if we moved the Camera.updateProjectionMatrix() to render() in an optimized way as I explained above about the needsUpdate.
Maybe we could include a Camera.projectionMatrixAutoUpdate = true to preserve compatibility... that last one is just an idea that just came to me.
The scenario I came across recently was the WebGPU hack: https://github.com/mrdoob/three.js/blob/c0f9764c0224e97f33f2487c4123b96429e18d21/examples/jsm/renderers/webgpu/WebGPUBackend.js#L98
Being applied only after WebGPU.init() have been completed. The problem is that until this happens the Camera.updateProjectionMatrix() has already been computed using the WebGL version. The suggestion above would solve this.