mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.63k stars 35.37k forks source link

WebGPU module js build of Three.js #25947

Closed andreasrosdal closed 4 months ago

andreasrosdal commented 1 year ago

Description

I would like a WebGPU build of Three.js in the /build directory. For example: three.webgpu.module.min.js So that I can begin testing and implementing WebGPU support.

At the moment WebGL is hidden away in this directory: https://github.com/mrdoob/three.js/blob/dev/examples/jsm/renderers/webgpu/WebGPURenderer.js

Solution

three.webgpu.module.min.js

Alternatives

Alternative: Include WebGPU in default module js build.

Additional context

No response

Mugen87 commented 1 year ago

Um, what do you mean with "hidden away"? You can import the renderer like any other addon.

andreasrosdal commented 1 year ago

The WebGPURenderer isn't included in the Three.js module js build. Instead it is stored in the /examples/jsm/renderers/webgpu/ subdirectory, and imports many other js files with specific relative paths.

So I wish that the WebGPU support with the WebGPURenderer was either included fully in the default module build of Three.js, or that there was a specific build of Three.js with WebGPU support.

donmccurdy commented 1 year ago

I don't think we should do anything immediately here, this approach is how many other important parts of three.js are packaged. But it would be worth planning a longer-term roadmap for WebGPU support; it will not be an addon forever.

ghost commented 1 year ago

@andreasrosdal are you building using default vite or using own setup? I'm asking because I'm experiencing issues building with vite (development version runs but production version is not).

Sorry for off-topic I'm relatively new to Threejs and WebGPU and want to verify that his is not an issue on my side.

andreasrosdal commented 1 year ago

@rv4Flyver Fciv.net imports Three.js as a module directly, with a workaround. See the source code here: https://github.com/fciv-net/fciv-net/blob/main/freeciv-web/src/main/webapp/webclient/index.jsp#L58

The rest of the source code is built with Maven and Closure compiler.

I would like to use a more modern build system.

makc commented 1 year ago

@andreasrosdal since you host the file yourself I dont see why cant you just build the version of 3js you want yourself? by editing this file and running standard 3js build, for example (this would not work because of circular dependency of three on itself).

danrossi commented 1 year ago

I've tried to look at this. Its referencing the module I create a custom build from sources as I found duplicates trying to reference the module when adding other imports.

The renderer tries to setup webgpu immediately with an async and not in the init method and patch Matrix4 in the init method. This breaks in an iife package. It needs to be setup in the init method.

andreasrosdal commented 1 year ago

I've tried to look at this. Its referencing the module I create a custom build from sources as I found duplicates trying to reference the module when adding other imports.

The renderer tries to setup webgpu immediately with an async and not in the init method and patch Matrix4 in the init method. This breaks in an iife package. It needs to be setup in the init method.

Cool, can you provide a WebGPU module build of Three.js?

makc commented 1 year ago

@andreasrosdal why are you so lazy 🤣 but okay, try this js file:

import * as THREE from './three.gpu.js';
console.log(THREE);

three.gpu.js.zip

makc commented 1 year ago

(all the Nodes.js stuff should be under THREE.Nodes)

danrossi commented 1 year ago

I'm working on figuring out how to make my custom three build as it references sources to stop duplicated imports that happens using the module. The code keeps making references to the module so might have to keep it that way. But there is refactoring required so it doesn't immediately setup webgpu. I may have to make a build of webgpu and reference my custom iife module build that includes custom components as global.

Here is refactoring snippets

patchApi() {

        Matrix4.prototype.makePerspective = makePerspective;
        Matrix4.prototype.makeOrthographic = makeOrthographic;
        Frustum.prototype.setFromProjectionMatrix = setFromProjectionMatrix;
        _frustum = new Frustum();
        _projScreenMatrix = new Matrix4();
        _vector3 = new Vector3();
    }

    async init() {

        if ( navigator.gpu !== undefined ) {

            staticAdapter = await navigator.gpu.requestAdapter();

        }

        this.patchApi();

Await when bundled causes an issue when trying to make a umd/iife bundle. Needs to be in init

 staticAdapter = await navigator.gpu.requestAdapter();

And then I can do

  if ( WebGPU.isAvailable()) {
                    renderer = new THREE.WebGPURenderer();
                    renderer.init();
                    console.log("using webgpu renderer");
                } else {
                    renderer = new THREE.WebGLRenderer();
                }
danrossi commented 1 year ago

Referencing the module to bundle the files, causes no files to be included and rollup just bundles the whole entire module. Its wierd. Only source includes works for me. But the webgpu files reference the module. I may have to fork that portion to use source includes also.

includePaths({
                paths: ["../three.js/src", "../three.js/examples/jsm"],
                include: {
                  'three': '../three.js/build/three.module.js'
                }
            }),

The WebGPU support tries to setup webgpu globally in the bundle. await breaks iife bundling. So support api check needs to be changed into a method.


if ( navigator.gpu !== undefined ) {

    const adapter = await navigator.gpu.requestAdapter();

    if ( adapter !== null ) {

        isAvailable = true;

    }

}
makc commented 1 year ago

@danrossi

So support api check needs to be changed into a method.

why not just make a separate PR for this (separate from this issue, that is)

danrossi commented 1 year ago

Understood. I've setup this build project for now with the mods. I've got to test it still.

https://github.com/danrossi/three-webgpu-renderer/tree/master/build

The modded files

https://github.com/danrossi/three-webgpu-renderer/blob/master/src/renderers/webgpu/WebGPURenderer.js https://github.com/danrossi/three-webgpu-renderer/blob/master/src/capabilities/WebGPU.js

danrossi commented 1 year ago

There is other problems I found. This gets bundled up with a tonne of stuff not needed normally for webglrenderer to support both and is run in the iife bundle but broken when compressing with terser. gets an error thrown. Should only be run when setting up the webgpurenderer if it needs it at all.

https://github.com/mrdoob/three.js/blob/d732ceb51ab154b870b01b940e550b4fdb2b5ea5/examples/jsm/nodes/core/Node.js#L429

It's caused by a circular dependancy when trying to bundle it. Claims to not be a class when compressing because it has no class name.

(!) Circular dependency
three.js/examples/jsm/nodes/lighting/AnalyticLightNode.js -> three.js/examples/jsm/nodes/materials/MeshBasicNodeMaterial.js -> three.js/examples/jsm/nodes/materials/NodeMaterial.js -> three.js/examples/jsm/nodes/lighting/LightsNode.js -> three.js/examples/jsm/nodes/lighting/AnalyticLightNode.js
danrossi commented 1 year ago

I'm hoping that external project works. I did test it and realised I have to change alot of the integration, I use RawShaderMaterial with custom shader and it doesn't like that. I have to try and support both WebGLRenderer and WebGPURenderer. I may need to modify how the nodes are loaded. They all get initialized at once and may not all be needed or included. The bundle with WebGPU is 1.6mb compared to 600k. I'm looking at it once I can fix the WebXR polyfill as it seems threejs breaks it, not the other way.

andreasrosdal commented 1 year ago

@danrossi Thank you for making this! Is this build ready for me try test in www.Fciv.net?

What about Water2, Gltfimporter and other addons I use?

danrossi commented 1 year ago

I haven't been able to properly test it yet outside the examples as stuff I use doesn't work with the renderer. There is a circular dependency bug that may prevent it working but import the built module and give it a try. The one bundled with three is huge, a 1.6mb bundle.

andreasrosdal commented 1 year ago

I get this error: Uncaught referenceError: GPUShaderStage is not defined

when using this build: https://github.com/danrossi/three-webgpu-renderer/blob/master/build/three-webgpu.module.js

image image

Can I please have just one js module build file? Why is there a webgpu-renderer.module.js

mrdoob commented 1 year ago

@andreasrosdal What version of Chrome is that and what OS?

andreasrosdal commented 1 year ago

Chromium Version 112 on OpenSUSE tumbleweed. I realize now that it doesn't support WebGPU yet. So I can try to use Google Chrome instead.

sunag commented 1 year ago

https://github.com/mrdoob/three.js/blob/517ce63ecc63e675643af6f7c16e4c94092b3e7f/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js#L20-L26

This code snippet is currently deprecated, I think it would be nice to update the code, important updates have been made.

mrdoob commented 1 year ago

Do we still need this?

https://github.com/mrdoob/three.js/blob/95f678c9ca63fdb500e9c635d780428cb687855a/examples/jsm/capabilities/WebGPU.js#L1-L5

sunag commented 1 year ago

Do we still need this?

Hmm... maybe not. I would just leave it until I make a GPU layout generation system, auto layout is limiting shadow integration.

danrossi commented 1 year ago

I'm looking back into the external module bundle as I believe WebGPU may be where we have to move to. However the bundle is huge I think and includes all the nodes one may not need. I'm going to figure something out to make it selective. The support check needs to be inside a method to await for a start where the api patch happens. So fallback support can be made for WebGLRenderer. The external async breaks in an iife bundle.

https://github.com/danrossi/three-webgpu-renderer/blob/master/src/renderers/webgpu/WebGPURenderer.js#L190

marcofugaro commented 1 year ago

@danrossi can you share a project demo with your setup? The one making the iife bundle

danrossi commented 1 year ago

I'm rebundling the module due to a refactor of the webgpu api. And this is the rollup output trying anything other than esm output. I'll have a look updating the rendererer in my build. But both files might have to be moved across to do it.

[!] Error: Module format iife does not support top-level await. Use the "es" or "system" output formats rather.
three.js/examples/jsm/renderers/webgpu/WebGPUBackend.js
Error: Module format iife does not support top-level await. Use the "es" or "system" output formats rather.
danrossi commented 1 year ago

I've updated the bundles and moving the async into init allows for an iife bundle. Ive yet to test it until I can figure out how to setup for equi and stereo video. I use rawshadermetarial with a trimmed down shader with no lighting and it doesn't like that.

https://github.com/danrossi/three-webgpu-renderer/blob/master/src/renderers/webgpu/WebGPUBackend.js#L66

danrossi commented 1 year ago

I added some tests with the build and seems to work. The support check does an await with my modified WebGPU class. I need to figure out how to get everything I have integrated working with this webgpu api. more specifically sphere geometry and video textures and rawshadermaterial. This example is adding an image texture to a background.

https://github.com/danrossi/three-webgpu-renderer/blob/master/tests/webgpu_equirectangular.html

danrossi commented 1 year ago

I've integrated the tests I need personally. So the renderer is a drop in replacement without differences to webgl renderer or does it need to use the texture and uv methods from nodes ?

I have an RTX2060 and with webgl I see 13% usage. With WebGPU it's 3% usage. The colours are washed out and high contrasted compared to WebGL.

And seems there is no xr manager yet for it so XR hasn't been implemented for WebGPU. This doesnt exist setReferenceSpaceType I've seen how that might possibly be done below. So not 100% usable until XR support ?

https://github.com/danrossi/three-webgpu-renderer/blob/master/tests/webgpu_video_panorama_equirectangular.html https://github.com/danrossi/three-webgpu-renderer/blob/master/tests/webgpu_webxr_vr_video.html

https://github.com/immersive-web/WebXR-WebGPU-Binding/blob/main/explainer.md

danrossi commented 1 year ago

I've tested the renderer with troika and got 30-70% gpu usage compared to webgl of 3% usage with only a few image textures added before video texture grids. So it's the reverse. I have to make a ticket about the washed out colours first im not sure if its the renderer or troika to make a ticket about that.

AdrianScott commented 1 year ago

How do the webgpu examples on threejs.org work then? Shouldn't it be possible to create them as independent projects or something?

mrdoob commented 1 year ago

@danrossi What's the current bundle size of a cube renderered using WebGPURenderer?

danrossi commented 1 year ago

The module minified with required node classes added to Terser exclusion is 209kb. with a full three build its 800kb. The loading of nodes should be selective somehow or selective exported in a module without having to copy files to override features. They all get registered on load of the page regardless if they are needed. It may slow page load.

https://github.com/danrossi/three-webgpu-renderer/blob/master/build/webgpu-renderer.module.min.js https://github.com/danrossi/three-webgpu-renderer/blob/master/build/three-webgpu.min.js

with three 1.56MB

https://github.com/danrossi/three-webgpu-renderer/blob/master/build/three-webgpu.module.js

832kb minified

https://github.com/danrossi/three-webgpu-renderer/blob/master/build/three-webgpu.module.min.js

I have it bundled in a troika build project. But I can't use it yet until the video texture performance issue is corrected.

A selective three build bundle is 1.23MB

https://github.com/danrossi/three-troika/blob/main/build/three/three.module.js https://github.com/danrossi/three-troika/blob/main/threelib.js

The very end bundle package is an iife. Why I have to make a copy of the gpubackend with my fix. Although that usage code again is within another iife package. end user only has to include the script on the page and configure things for the application I built with it. Its for a webcam video grid to merge webrtc conferencing streams to one stream with graphical elements. And then webrtc publish that canvas stream with audio mixed in an audio destination. Most people out there demonstrate canvas software rendering. Each video texture added adds resources so was hoping to test webgpu.

Expecting modules and imports to setup things is difficult for end users with variable technical abilities and the imported code is usually done internally to set things up. I have to deal with devs and non devs.

https://github.com/danrossi/three-troika/blob/main/test/index.html

mrdoob commented 1 year ago

The module minified with required node classes added to Terser exclusion is 209kb.

Nice! Is that gzipped?

danrossi commented 1 year ago

The module minified with required node classes added to Terser exclusion is 209kb.

Nice! Is that gzipped?

It's just terser minified. I can try a zipped option. When deploying to s3 in my build scripts I gzip files while uploading.

danrossi commented 1 year ago

The file minified then compressed with zopfli is 55kb. and 200kb for the three bundle. Are these importable served gzipped ? I don't know how to test the gzip yet.

https://github.com/danrossi/three-webgpu-renderer/blob/master/build/webgpu-renderer.module.min.js.gz https://github.com/danrossi/three-webgpu-renderer/blob/master/build/three-webgpu.module.min.js.gz

mrdoob commented 1 year ago

If you have a terminal you can do gzip bundle.js and it will produce a bundle.js.gz.

danrossi commented 1 year ago

I have a zopfli compression build in rollup. That produces those files above. I mean it has to be setup to be served with the correct mimetype.

andreasrosdal commented 1 year ago

Great work on this WebGPU build. Is this build now in such a working state that I can begin using this?

danrossi commented 1 year ago

You can fork and do whatever you like with it as long as the submodule is updated during the build. One thing to note they are still adding materials. Not all materials support is there yet and I don't know enough to add them myself. You can make a ticket to request maybe.

But in my version I have to make a duplicated version of the backend so it can work in iife bundles and I have to merge changes each time.

https://github.com/danrossi/three-webgpu-renderer/blob/master/src/renderers/webgpu/WebGPUBackend.js

Which is still the most common bundle for applications. I cant release my stuff as modules personally as I deal with people of variable technical abilities. I made a PR to fix the top level async issue as webgpu will be requested when loading the module before even checking for webgpu support to fallback to WebGL so a logical issue. but it seems its not accepted and people are going to be forced to release applications as modules.

I haven't had the time to figure out how to bundle as a module and work like iife with a namespace yet. I'm on rebuilding a legacy XR polyfill for Iphone with a stereo distortion effect.

There is still performance issues with video textures. The cpu issue has been fixed and now consumes 1-2% cpu but it now consumes 20-40% gpu for video textures. It requires digging through each call stage to figure out why.

https://danrossi.github.io/three-webgpu-renderer/tests/webgpu_video_panorama_equirectangular.html

mrdoob commented 1 year ago

The file minified then compressed with zopfli is 55kb. and 200kb for the three bundle.

Why are there two files? Aren't we trying to produce a single build?

danrossi commented 1 year ago

There is a bundle with just the renderer module. And one with three built into it if anyone needs it. An example project that takes the module in via npm. Although I can't activate it yet until a performance problem is fixed.

https://github.com/danrossi/three-troika/blob/main/package.json#L20

AdrianScott commented 1 year ago

Thanks for all of the help, Dan, and Mr Doob and team for Three.js. We were able to get this published onto the Bitcoin blockchain. Looks like it's the first Ordinals/NFT collection using WebGPU, built on top of Three.js and the work of many great folks: https://ordinals.com/content/7dbc067eb7a692c9811cbff25b23ef82b4329cbcc2b729d05f493db887a19744i0

(hit 'm' to start the spatial audio, lots of other keys do things; more info https://twitter.com/Anarkoic/status/1684370030060769282 )

We basically did a build using Dan Rossi's three-gpu-renderer repo, but had to use a really limited subset of three.js, e.g. basic materials etc. Need to get around to writing it up a bit more. Cheers.

danrossi commented 1 year ago

It's ok not a problem. Just avoid video textures with webgpu and fallback to webgl for now. I tried my best very to pin point the high gpu usage problem. Where the issue I discovered is binding a group, I don't understand how it's different to the vanilla code.

andreasrosdal commented 11 months ago

Thanks for this great effort in making a WebGPU build of Three.js.

Can three-webgpu-renderer please be updated to the latest Three.js version?

Is it perhaps time now to get a official build of Three.js with WebGPU support in the Three.js repo?

Thank you!

andreasrosdal commented 11 months ago

One official build of Three.js with support for both WebGPU and WebGL 2 would be ideal.

danrossi commented 11 months ago

I'm sorry mate it's mine. You can fork and run the build yourself. I try to check in and keep it up to date. But to be absolutely honest I am personally having issues finding regular customers right now and issues at home to keep up with this.

So an automatic build matching my rollup config might be more useful I guess. I found I had to export ALL the nodes exports to be useful for using with the node system. All those math and shader method exports. I am slowly trying to figure out the node system myself and is hard figuring out the vertex part trying to replicate a third party shader I am force to migrate myself. Taking ages.

danrossi commented 11 months ago

done. I might have accidently committed a node system test I am working on.

danrossi commented 11 months ago

Something has been reverted so now have high gpu usage with video textures again I have to report.