Skip to content

Conversation

@sunag
Copy link
Collaborator

@sunag sunag commented Mar 31, 2023

Description

One of the main problems of WebGPURenderer today is the caching system. I recreated the cache system oriented to a WebGPUWeakMap where the keys are terminated by an array that defines the unique flow of the Shader.

function getChainKeys( object, material, scene, camera, lightsNode ) {

	// use dummy camera for optimize cache in case of use others cameras with the same type
	return [ object, material, scene, dummyCameras[ camera.type ], lightsNode ];

}

// renderer already included in this context
const rendererObjectContext = getChainKeys( object, material, scene, camera, lightsNode );

const cache = new WebGPUWeakMap();
cache.set( rendererObjectContext, new WebGPURenderObject( ... ) );

WebGPURenderObject it is the class that determines all possible variations of bindings, pipeline and nodes in relation to the object, it also allows obtaining a CacheKey for revalidation if necessary.

Moves geometry updating from WebGLObjects to WebGPUGeometries.

scene.overrideMaterial is now available for use.

Demos

Demo 1
https://raw.githack.com/sunag/three.js/dev-webgpu-cache-system-demo/examples/demo1.html

const material = new MeshBasicNodeMaterial();
material.colorNode = smoothstep( camera.near, camera.far, positionView.z.negate() ).oneMinus();

scene.overrideMaterial = material;

setTimeout( () => {
	
	scene.overrideMaterial = null;

	setTimeout( () => {

		scene.environment = cube2Texture;
		scene.background = cube2Texture;

		setTimeout( () => {

			scene.environmentNode = environmentNode;
			scene.backgroundNode = backgroundNode;

		}, 2000 );

	}, 2000 );

}, 2000 )

Demo 2
https://raw.githack.com/sunag/three.js/dev-webgpu-cache-system-demo/examples/demo1.html

const loopDemo = () => {

	setTimeout( () => {
			
		const light = new THREE.PointLight( 0xffffff, 300, 100000 );
		camera.add( light );

		setTimeout( () => {

			camera.remove( light );

			loopDemo();

		}, 500 )

	}, 500 )

}

loopDemo();

@sunag sunag added this to the r152 milestone Mar 31, 2023
@Mugen87
Copy link
Collaborator

Mugen87 commented Apr 1, 2023

Great PR! I'll review this on Monday!

@sunag
Copy link
Collaborator Author

sunag commented Apr 1, 2023

Some important things:

  • We cannot cache binds created in other pipelines, every time a pipeline is removed must remove the binds created by it.
    ( it can be because of getBindGroupLayout() generated the layout automatically, I need to investigate this more )

  • We should make the Node's getCacheKey() base on the type for InputNode instead of the UUID, this will allow more caching of the NodeMaterial created.


}

getEnvironmentNode() {
Copy link
Collaborator

@Mugen87 Mugen87 Apr 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR looks great so far! The only thing that I have noticed is that some node-specific logic/semantic finds its way into the renderer.

The original idea was to make the renderer material-agnostic (as good as possible) so it does not rely on a specific material system. But now there are things like getEnvironmentNode() or getFogNode() (so node syntax) inside renderer classes.

Would it be possible to reduce the node related logic inside WebGPURenderer to a minimum?

The idea would be to define more general interfaces in the renderer (e.g. getEnvironment()) and then inject a material specific object (e.g. the nodes object that we already used elsewhere) that provides the implementation. In our case it is node specific but in theory it could be something different, too.

The overall design goal is to avoid a too tight coupling of the renderer to the node material.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it ideally should be something like this:
WebGLRenderer/WebGPURenderer/SoftwareRenderer/another renderer (doesn't know about nodes, takes shaders written in GLSL/WGSL/JS) -- NodeRenderer (a boilerplate "renderer" that knows how to work with nodes) and WebGLNodeBuilder/WebGPUNodeBuilder/JSNodeBuilder (converts node material to GLSL/WGSL/JS shader) -- NodeMaterial system -- Nodes system.
So here getEnvironmentNode and getFogNode could go exactly in the NodeRenderer.

Copy link
Owner

@mrdoob mrdoob Apr 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current goals are these:

  1. WebGPURenderer should be able to replace WebGLRenderer (except ShaderMaterial).
  2. WebGPURenderer should have a WebGPUBackend and a WebGLBackend.
  3. This summer, the <model-viewer> project has to be able to replace WebGLRenderer with WebGPURenderer easily so it can use WebGPU when available.
  4. After replacing the renderer, <model-viewer>'s file size should stay relatively the same.

We should be careful with any additional work on the nodes system that gets in the way of achieving these goals.

Copy link
Contributor

@LeviPesin LeviPesin Apr 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we need WebGL backend for WebGPU renderer? Why we can't make (almost) completely interchangeable WebGLRenderer and WebGPURenderer?

Copy link
Owner

@mrdoob mrdoob Apr 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we can't ask developers to start using two renderers in their projects and do the feature detection themselves. Resulting in 2x bigger build sizes too.

The original goal of the nodes code was to solve the tree-shaking problem of the WebGLRenderer by replacing the non tree-shakeable ShaderChunks with the tree-shakeable NodeMaterial.

The fact that NodeMaterial is able to produce GLSL and WGSL makes it much easier to have the renderer support both back-ends..

And developers should not have to know any of these details.

It seems to me like the best plan is to say that... as long as their project doesn't use ShaderMaterial... we can promise them that they can start using WebGPURenderer in their projects if they want to get WebGPU performance benefits when available, yet the renderer will take care of the backwards compatibility for them when not available.

So, the community's developer experience I'm aiming for is this:

  1. Same scene graph API.
  2. The only thing that changes is the renderer.
  3. Their projects will automatically support WebGL and WebGPU.
  4. If NodeMaterial is able to keep its promise, their bundle sizes should be smaller.

We may need to create a separate npm package though... three-webgpu or something.

However, depending on WebGPU support, we could then remove the WebGLBackend at some point in the future.

Just how WebGLRenderer now uses WebGL 2 by default yet it is still able to use WebGL 1 when not available to make the developers life easier (instead of making them use a WebGLRenderer and a WebGL2Renderer).

Copy link
Collaborator Author

@sunag sunag Apr 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could consider that possibility?

const backend = WebGPU.isAvailable() ? new WebGPUBackend() : new WebGLBackend();

const renderer = new THREE.Renderer( backend );

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps if the backend was compatible with Raytracing as well like:
But I don't know if it would be possible, just a thought

const renderer = new THREE.Renderer( new WebGPURaytracingBackend() );

Copy link
Owner

@mrdoob mrdoob Apr 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API and its usage stays the same. The only thing that changes is the name of the renderer's class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const backend = WebGPU.isAvailable() ? new WebGPUBackend() : new WebGLBackend(); const renderer = new THREE.Renderer( backend );

That's acceptable, I think -- but const renderer = WebGPU.isAvailable() ? new WebGPURenderer() : new WebGLNodeRenderer() is semantically the same but little bit simpler. But I don't oppose this approach.

Copy link
Owner

@mrdoob mrdoob Apr 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's basically the same WebGPURenderer will do internally.

const backend = WebGPU.isAvailable() ? new WebGPUBackend() : new WebGLBackend();

@sunag
Copy link
Collaborator Author

sunag commented Apr 3, 2023

@Mugen87

The original idea was to make the renderer material-agnostic (as good as possible) so it does not rely on a specific material system. But now there are things like getEnvironmentNode() or getFogNode() (so node syntax) inside renderer classes.

I think it gets more organized too, updated.

  • These nodes are part of the environment nodes ( scene + renderer ) not the material.

@sunag
Copy link
Collaborator Author

sunag commented Apr 4, 2023

Now we have getEnvironmentNode(), getFogNode() and getToneMappingNode() nodes moved to WebGPUNodes that are shared among all scene materials in the context of getChainKeys(), the renderer remains material-agnostic, but we must consider that some nodes are part of environment variables because they are part of scene.fog, scene.environment and renderer.toneMapping. I think that extinguishing these nodes would be a bad optimization and would complicate the validation of hashes.

@sunag sunag mentioned this pull request Apr 4, 2023
1 task
@Mugen87
Copy link
Collaborator

Mugen87 commented Apr 4, 2023

@sunag That sounds like a good compromise!

@sunag
Copy link
Collaborator Author

sunag commented Apr 4, 2023

@Mugen87 Thanks :)
Merging...

@sunag sunag merged commit 8cc9f32 into mrdoob:dev Apr 4, 2023
@sunag sunag deleted the dev-webgpu-cache-system branch April 6, 2023 19:24
@sunag sunag mentioned this pull request May 18, 2023
6 tasks
@LeviPesin LeviPesin mentioned this pull request Sep 5, 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.

4 participants