-
-
Notifications
You must be signed in to change notification settings - Fork 36.1k
Backend-agnostic GPGPU on Nodes #25273
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
Closed
Closed
Changes from 2 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
2b056f8
Initial port
LeviPesin 0a182f1
Clean up (includes TODO 1)
LeviPesin fdd2250
Fixes
LeviPesin c86b396
Fix screenshot
LeviPesin 6f0fbce
WebGLBuffer (TODO 2). Doesn't really work
LeviPesin 0a69697
Some nodes refactor
LeviPesin cbf104b
Fix (still not working) (TODO 3)
LeviPesin bd312e7
Remove GH comments from code
LeviPesin 19d896d
Some cleanup
LeviPesin 5871996
Fix framebuffer loop problem
LeviPesin b489298
Fix why other examples weren't rendering
LeviPesin 96be961
Fix WebGLNodeBuilder.getInstanceIndex()
LeviPesin 7d9e040
Fix other Nodes examples that broke
LeviPesin ddf51c7
Fix
LeviPesin 4ca19cf
Fix
LeviPesin a73ba79
Little bit more accurate measurements
LeviPesin 39fed20
Fix ViewportNode
LeviPesin e2db2e5
Fix bypass
LeviPesin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| export default class ComputationRenderer { | ||
|
|
||
| constructor( renderer ) { | ||
|
|
||
| this.renderer = renderer; | ||
|
|
||
| this._buffers = []; | ||
|
|
||
| } | ||
|
|
||
| createBuffer( /* ...params */ ) { | ||
|
|
||
| console.warn( 'Abstract function.' ); | ||
| // this._buffers.push( buffer ); | ||
|
|
||
| } | ||
|
|
||
| async compute( /* shaderNode, outBuffer, populateTypedArray = true */ ) { | ||
|
|
||
| console.warn( 'Abstract function.' ); | ||
|
|
||
| } | ||
|
|
||
| disposeBuffers() { | ||
|
|
||
| this._buffers.forEach( buffer => buffer.dispose() ); | ||
|
|
||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| import { float, int, uint, vec2, ivec2, uvec2, vec3, ivec3, uvec3, vec4, ivec4, uvec4 } from 'three/nodes'; | ||
|
|
||
| export default class TypedBuffer { | ||
|
|
||
| constructor( bufferAttribute ) { | ||
|
|
||
| if ( ( bufferAttribute.array instanceof Float64Array ) || ( bufferAttribute.array instanceof BigInt64Array ) || ( bufferAttribute.array instanceof BigUint64Array ) ) { | ||
|
|
||
| throw new Error( 'Float64Array, BigInt64Array, and BigUint64Array are not supported, because float64 and int64 are not supported either in WebGL or WebGPU' ); | ||
|
|
||
| } | ||
|
|
||
| this._attribute = bufferAttribute; | ||
| this._buffer = null; | ||
|
|
||
| } | ||
|
|
||
| get typedArray() { | ||
|
|
||
| return this._attribute.array; | ||
|
|
||
| } | ||
|
|
||
| set typedArray( typedArray ) { | ||
|
|
||
| this._attribute.array.set( new this._attribute.array.constructor( typedArray ) ); | ||
|
|
||
| } | ||
|
|
||
| get buffer() { | ||
|
|
||
| return this._buffer; | ||
|
|
||
| } | ||
|
|
||
| set buffer( value ) { | ||
|
|
||
| throw new Error( 'GPU buffer of a TypedBuffer cannot be changed' ); | ||
|
|
||
| } | ||
|
|
||
| get arrayLength() { | ||
|
|
||
| return this._attribute.array.length; | ||
|
|
||
| } | ||
|
|
||
| get length() { | ||
|
|
||
| return this._attribute.count; | ||
|
|
||
| } | ||
|
|
||
| getBufferElement( /* i */ ) { | ||
|
|
||
| console.warn( 'Abstract function.' ); | ||
|
|
||
| } | ||
|
|
||
| setBufferElement( /* i, value */ ) { | ||
|
|
||
| console.warn( 'Abstract function.' ); | ||
|
|
||
| } | ||
|
|
||
| set needsUpdate( value ) { | ||
|
|
||
| this._buffer.needsUpdate = value; | ||
|
|
||
| } | ||
|
|
||
| dispose() { | ||
|
|
||
| this._buffer.dispose(); | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| export function getFunction( bufferAttribute ) { | ||
|
|
||
| const array = bufferAttribute.array; | ||
|
|
||
| let functionType; | ||
|
|
||
| if ( ( array instanceof Int8Array ) || ( array instanceof Int16Array ) || ( array instanceof Int32Array ) ) { | ||
|
|
||
| functionType = 'int'; | ||
|
|
||
| } else if ( ( array instanceof Uint8Array ) || ( array instanceof Uint8ClampedArray ) || ( array instanceof Uint16Array ) || ( array instanceof Uint32Array ) ) { | ||
|
|
||
| functionType = 'uint'; | ||
|
|
||
| } else if ( array instanceof Float32Array ) { | ||
|
|
||
| functionType = 'float'; | ||
|
|
||
| } | ||
|
|
||
| switch ( bufferAttribute.itemSize ) { | ||
|
|
||
| case 1: | ||
| return ( functionType === 'uint' ) ? uint : ( functionType === 'int' ) ? int : float; | ||
|
|
||
| case 2: | ||
| return ( functionType === 'uint' ) ? uvec2 : ( functionType === 'int' ) ? ivec2 : vec2; | ||
|
|
||
| case 3: | ||
| return ( functionType === 'uint' ) ? uvec3 : ( functionType === 'int' ) ? ivec3 : vec3; | ||
|
|
||
| case 4: | ||
| return ( functionType === 'uint' ) ? uvec4 : ( functionType === 'int' ) ? ivec4 : vec4; | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| export function getFloatFunction( bufferAttribute ) { | ||
|
|
||
| switch ( bufferAttribute.itemSize ) { | ||
|
|
||
| case 1: | ||
| return float; | ||
|
|
||
| case 2: | ||
| return vec2; | ||
|
|
||
| case 3: | ||
| return vec3; | ||
|
|
||
| case 4: | ||
| return vec4; | ||
|
|
||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import { Camera, Mesh, PlaneGeometry, Scene, WebGLRenderTarget } from 'three'; | ||
| import { int, viewportCoordinate, MeshBasicNodeMaterial, ShaderNode } from 'three/nodes'; | ||
| import { nodeFrame } from 'three/addons/renderers/webgl/nodes/WebGLNodes.js'; | ||
| import ComputationRenderer from './ComputationRenderer.js'; | ||
| import WebGLTypedBuffer from './WebGLTypedBuffer.js'; | ||
|
|
||
| export default class WebGLComputationRenderer extends ComputationRenderer { | ||
|
|
||
| constructor( renderer ) { | ||
|
|
||
| super( renderer ); | ||
|
|
||
| this._material = new MeshBasicNodeMaterial(); | ||
| this._scene = new Scene().add( new Mesh( new PlaneGeometry( 2, 2 ), this._material ) ); | ||
| this._camera = new Camera(); | ||
|
|
||
| } | ||
|
|
||
| createBuffer( ...params ) { | ||
|
|
||
| const buffer = new WebGLTypedBuffer( ...params ); | ||
| this._buffers.push( buffer ); | ||
| return buffer; | ||
|
|
||
| } | ||
|
|
||
| async compute( shaderNode, outBuffer, populateTypedArray = true ) { | ||
|
|
||
| nodeFrame.update(); | ||
|
|
||
| const outGPUBuffer = outBuffer.buffer; | ||
| outGPUBuffer.isRenderTargetTexture = true; | ||
| const outTypedArray = outBuffer.typedArray; | ||
|
|
||
| const renderTarget = new WebGLRenderTarget( outGPUBuffer.image.width, outGPUBuffer.image.height, { depthBuffer: false } ); | ||
| renderTarget.texture = outGPUBuffer; | ||
|
|
||
| const index = int( viewportCoordinate.y ).mul( outGPUBuffer.image.width ).add( int( viewportCoordinate.x ) ); | ||
| this._material.colorNode = new ShaderNode( ( inputs, builder ) => { | ||
|
|
||
| return outBuffer.setBufferElement( index, shaderNode.call( { index }, builder ) ); | ||
|
|
||
| } ); | ||
| this._material.needsUpdate = true; | ||
|
|
||
| const currentRenderTarget = this.renderer.getRenderTarget(); | ||
| this.renderer.setRenderTarget( renderTarget ); | ||
| this.renderer.render( this._scene, this._camera ); | ||
| if ( populateTypedArray ) { | ||
|
|
||
| this.renderer.readRenderTargetPixels( renderTarget, 0, 0, renderTarget.width, renderTarget.height, outTypedArray ); | ||
| // The .render call populates the GPU buffer, the .readRenderTargetPixels call populates the typed array | ||
|
|
||
| } else { | ||
|
|
||
| outBuffer.typedArray = outTypedArray.length; // null array | ||
|
|
||
| } | ||
| this.renderer.setRenderTarget( currentRenderTarget ); | ||
|
|
||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| import { | ||
| DataTexture, | ||
|
|
||
| UnsignedByteType, | ||
| ByteType, | ||
| ShortType, | ||
| UnsignedShortType, | ||
| IntType, | ||
| UnsignedIntType, | ||
| FloatType, | ||
|
|
||
| RedFormat, | ||
| RGFormat, | ||
| RGBAFormat | ||
| } from 'three'; | ||
| import { texture, add, div, remainder, floor, vec2, mul, float, int } from 'three/nodes'; | ||
| import TypedBuffer, { getFunction, getFloatFunction } from './TypedBuffer.js'; | ||
|
|
||
| function getTextureElement( dataTexture, i, width, height ) { | ||
|
|
||
| const x = div( add( 0.5, remainder( i, width ) ), width ); | ||
| const y = div( add( 0.5, div( i, width ) ), height ); | ||
|
|
||
| return texture( dataTexture, vec2( x, y ) ); | ||
|
|
||
| } | ||
|
|
||
| function getTextureType( bufferAttribute ) { | ||
|
|
||
| const array = bufferAttribute.array; | ||
|
|
||
| let textureType; | ||
|
|
||
| if ( array instanceof Int8Array ) { | ||
|
|
||
| textureType = ByteType; | ||
|
|
||
| } else if ( ( array instanceof Uint8Array ) || ( array instanceof Uint8ClampedArray ) ) { | ||
|
|
||
| textureType = UnsignedByteType; | ||
|
|
||
| } else if ( array instanceof Int16Array ) { | ||
|
|
||
| textureType = ShortType; | ||
|
|
||
| } else if ( array instanceof Uint16Array ) { | ||
|
|
||
| textureType = UnsignedShortType; | ||
|
|
||
| } else if ( array instanceof Int32Array ) { | ||
|
|
||
| textureType = IntType; | ||
|
|
||
| } else if ( array instanceof Uint32Array ) { | ||
|
|
||
| textureType = UnsignedIntType; | ||
|
|
||
| } else if ( array instanceof Float32Array ) { | ||
|
|
||
| textureType = FloatType; | ||
|
|
||
| } | ||
|
|
||
| return textureType; | ||
|
|
||
| } | ||
|
|
||
| export function getTextureFormat( bufferAttribute ) { // @TODO: possibly support impossible type-format combinations by using padding | ||
|
|
||
| switch ( bufferAttribute.itemSize ) { | ||
|
|
||
| case 1: | ||
| return RedFormat; | ||
|
|
||
| case 2: | ||
| return RGFormat; | ||
|
|
||
| case 3: | ||
| return null; | ||
|
|
||
| case 4: | ||
| return RGBAFormat; | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| export function calculateWidthHeight( length ) { | ||
|
|
||
| let width; // @TODO: maybe just set width and height to Math.sqrt( length ) and pad the texture with zeroes? | ||
| for ( width = Math.floor( Math.sqrt( length ) ); width > 0; width ++ ) { | ||
|
|
||
| if ( length % width === 0 ) break; | ||
|
|
||
| } | ||
|
||
| const height = length / width; | ||
|
|
||
| return { width, height }; | ||
|
|
||
| } | ||
|
|
||
| export default class WebGLTypedBuffer extends TypedBuffer { | ||
|
|
||
| constructor( bufferAttribute ) { | ||
|
|
||
| // @TODO: add support for UBOs | ||
|
|
||
| super( bufferAttribute ); | ||
|
|
||
| const { width, height } = calculateWidthHeight( this.length ); | ||
| this.width = width; | ||
| this.height = height; | ||
|
|
||
| const buffer = this._buffer = new DataTexture( this.typedArray, width, height, getTextureFormat( bufferAttribute ), getTextureType( bufferAttribute ) ); | ||
| buffer.needsUpdate = true; | ||
|
|
||
| this._function = getFunction( bufferAttribute ); | ||
| this._floatFunction = getFloatFunction( bufferAttribute ); | ||
|
|
||
| } | ||
|
|
||
| getBufferElement( i ) { | ||
|
|
||
| const gpuBuffer = this._buffer; | ||
| const textureElement = getTextureElement( gpuBuffer, int( i ), gpuBuffer.image.width, gpuBuffer.image.height ); | ||
| return this._function( mul( textureElement, 255 ) ); | ||
|
|
||
| } | ||
|
|
||
| setBufferElement( i, value ) { | ||
|
|
||
| return div( this._floatFunction( value ), 255 ); | ||
|
|
||
| } | ||
|
|
||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.