Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Examples: GPGPU Water Port #29147

Merged
merged 21 commits into from
Sep 8, 2024
Merged

Examples: GPGPU Water Port #29147

merged 21 commits into from
Sep 8, 2024

Conversation

cmhhelgeson
Copy link
Contributor

@cmhhelgeson cmhhelgeson commented Aug 15, 2024

Related issue: #XXXX

Description

Port of the WebGL GPGPU Water Sample.

EDIT 1: Sphere dynamics have been modified to leverage to used the positionNode shader over the CPU.

EDIT 2: Original WebGL sample slightly adjusted to demonstrate WebGL vs. WebGPU performance differential.

Compute.Water.mp4

examples/webgpu_compute_water.html Fixed Show fixed Hide fixed
examples/webgpu_compute_water.html Fixed Show fixed Hide fixed
examples/webgpu_compute_water.html Fixed Show fixed Hide fixed
examples/webgpu_compute_water.html Fixed Show fixed Hide fixed
examples/webgpu_compute_water.html Fixed Show fixed Hide fixed
examples/webgpu_compute_water.html Fixed Show fixed Hide fixed
examples/webgpu_compute_water.html Fixed Show fixed Hide fixed
examples/webgpu_compute_water.html Fixed Show fixed Hide fixed
examples/webgpu_compute_water.html Fixed Show fixed Hide fixed
examples/webgpu_compute_water.html Fixed Show fixed Hide fixed
examples/webgpu_compute_water.html Fixed Show fixed Hide fixed
Copy link

github-actions bot commented Aug 16, 2024

📦 Bundle size

Full ESM build, minified and gzipped.

Filesize dev Filesize PR Diff
685.4 kB (169.7 kB) 685.4 kB (169.7 kB) +0 B

🌳 Bundle size after tree-shaking

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

Filesize dev Filesize PR Diff
462 kB (111.4 kB) 462 kB (111.4 kB) +0 B

@cmhhelgeson
Copy link
Contributor Author

Kind of stumped by the WebGL Backend behavior here:

WebGL.Compute.Water.mp4

@cmhhelgeson cmhhelgeson marked this pull request as ready for review August 16, 2024 04:03
@cmhhelgeson cmhhelgeson marked this pull request as draft August 16, 2024 04:30
@cmhhelgeson cmhhelgeson marked this pull request as ready for review August 16, 2024 18:56
@cmhhelgeson cmhhelgeson marked this pull request as draft August 17, 2024 02:27
@cmhhelgeson
Copy link
Contributor Author

cmhhelgeson commented Aug 17, 2024

Think I understand the issue with the WebGL backend, it seems like the conversion to GLSL does not yet account for accessing the same buffer multiple times. For instance, this TSL code

const { viscosity, mousePos, mouseSize } = effectController;

const height = heightStorage.element( instanceIndex );
const prevHeight = prevHeightStorage.element( instanceIndex );

const { north, south, east, west } = getNeighborValuesTSL( instanceIndex, heightStorage );

const neighborHeight = north.add( south ).add( east ).add( west );
neighborHeight.mulAssign( 0.5 );
neighborHeight.subAssign( prevHeight );

const newHeight = neighborHeight.mul( viscosity );

Generates this GLSL output:

float nodeVar0;
float nodeVar1;

// transforms
nodeVarying0 = nodeAttribute0;
nodeVarying1 = nodeAttribute1;
	
// flow code

nodeVar0 = ( ( ( nodeVarying0 + nodeVarying0 ) + nodeVarying0 ) + nodeVarying0 );
nodeVar0 = ( nodeVar0 * 0.5 );
nodeVar0 = ( nodeVar0 - nodeVarying1 );
nodeVar1 = ( nodeVar0 * c_viscosity );

The first line of code corresponds with the getNeighborValuesTSL call but only the same height value it already read in. Presumably there's a workaround with storageObjects....

Equivalent WGSL output for comparison

// system
instanceIndex = id.x + id.y * numWorkgroups.x * u32(64) + id.z * numWorkgroups.x * numWorkgroups.y * u32(64);

// vars
	
var nodeVar0 : u32;
var nodeVar1 : u32;
var nodeVar2 : f32;
var nodeVar3 : f32;


// flow code

nodeVar0 = ( instanceIndex / 128u );
nodeVar1 = ( instanceIndex % 128u );
nodeVar2 = ( ( ( NodeBuffer_476.Height[ ( ( min( ( nodeVar0 + u32( 1.0 ) ), ( 128u - u32( 1.0 ) ) ) * 128u ) + nodeVar1 ) ] + 
     NodeBuffer_476.Height[ ( ( max( u32( 0.0 ), ( nodeVar0 - u32( 1.0 ) ) ) * 128u ) + nodeVar1 ) ] ) + NodeBuffer_476.Height[ ( ( 
     nodeVar0 * 128u ) + min( ( nodeVar1 + u32( 1.0 ) ), ( 128u - u32( 1.0 ) ) ) ) ] ) + NodeBuffer_476.Height[ ( ( nodeVar0 * 128u ) 
     + max( u32( 0.0 ), ( nodeVar1 - u32( 1.0 ) ) ) ) ] );
nodeVar2 = ( nodeVar2 * 0.5 );
nodeVar2 = ( nodeVar2 - NodeBuffer_477.PrevHeight[ instanceIndex ] );
nodeVar3 = ( nodeVar2 * object.viscosity );

@mrdoob
Copy link
Owner

mrdoob commented Aug 17, 2024

@RenaudRohlinger @sunag

@cmhhelgeson
Copy link
Contributor Author

cmhhelgeson commented Aug 17, 2024

With storage objects, the GLSL Node Builder accesses the PBO, but not in the right place EDIT: Converting the height and prevHeight values to vars puts inital texture accesses in the proper place. However, the NodeBuilder is still seemingly unable of accessing multiple texture values, as it needs to do when it creates nodeVar6 ( aka neighborHeight in our TSL ). I'll push the current version of the code that generates this TSL when forceWebGL is enabled.

layout( std140 ) uniform compute_object {
	float c_viscosity;
	vec2 c_mousePos;
	float c_mouseSize;
};
uniform highp sampler2D Height;
uniform highp sampler2D PrevHeight;

// varyings
out float nodeVarying0;
out float nodeVarying1;

// attributes
layout( location = 0 ) in float nodeAttribute2;
layout( location = 1 ) in float nodeAttribute6;

void main() {
        // variable declarations omitted

	// transforms
	nodeVarying0 = nodeAttribute2;
	nodeVarying1 = nodeAttribute6;
	
	// flow

	nodeVar1Size = uint( textureSize( Height, 0 ).x );
	nodeVar1 = vec4(texelFetch( Height, ivec2(uint( gl_InstanceID ) % nodeVar1Size, uint( gl_InstanceID ) / nodeVar1Size), 0 )).x;
	nodeVar0 = nodeVar1;
	nodeVar4Size = uint( textureSize( PrevHeight, 0 ).x );
	nodeVar4 = vec4(texelFetch( PrevHeight, ivec2(uint( gl_InstanceID ) % nodeVar4Size, uint( gl_InstanceID ) / nodeVar4Size), 0 )).x;
	nodeVar3 = nodeVar4;
        // Worrisome neighborHeight code
	nodeVar6 = ( ( ( nodeVarying0 + nodeVarying0 ) + nodeVarying0 ) + nodeVarying0 );
	nodeVar6 = ( nodeVar6 * 0.5 );

@cmhhelgeson cmhhelgeson marked this pull request as ready for review August 20, 2024 18:57
@mrdoob mrdoob requested a review from sunag September 5, 2024 08:22
@sunag sunag added this to the r169 milestone Sep 5, 2024
}

const heightBufferAttribute = new THREE.StorageBufferAttribute( heightArray, 1 );
const prevHeightBufferAttribute = new THREE.StorageBufferAttribute( prevHeightArray, 1 );
Copy link
Collaborator

Choose a reason for hiding this comment

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

StorageInstancedBufferAttribute

// To correct the lighting as our mesh undulates, we have to reassign the normals in the position shader.
const { normalX, normalY } = getNormalsFromHeightTSL( vertexIndex, heightRead );

varyingProperty( 'vec3', 'v_normalView' ).assign( modelNormalMatrix.mul( vec3( normalX, negate( normalY ), 1.0 ) ) );
Copy link
Collaborator

Choose a reason for hiding this comment

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

TSL now have a function for this: transformNormalToView( vec3( normalX, negate( normalY ), 1.0 ) )

Copy link
Contributor Author

@cmhhelgeson cmhhelgeson Sep 6, 2024

Choose a reason for hiding this comment

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

This seems to break the lighting when I test it, but I'll check back in on it tomorrow ( I am in PST ). Aren't modelNormalMatrix and modelNormalViewMatrix distinct? And if they aren't, should the name of the node be updated to reflect that?

@sunag sunag merged commit c5819a3 into mrdoob:dev Sep 8, 2024
11 checks passed
@cmhhelgeson cmhhelgeson deleted the webgpu_compute_water branch September 8, 2024 22:27
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