-
-
Notifications
You must be signed in to change notification settings - Fork 35.4k
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
Introduce storageStruct #29908
base: dev
Are you sure you want to change the base?
Introduce storageStruct #29908
Changes from 6 commits
52939b2
13461f0
0930b48
8ed8822
eefe48a
d76a0e6
b613aa8
273e588
5341a29
28fb56d
7850209
a072a6e
99218be
4d618cb
a512721
4882f9e
1f646fd
baa6d0c
9f9b70d
d704d92
6f5ec07
fb96b7e
4861c3a
3ea0f3a
f49feaa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -964,6 +964,88 @@ | |
|
||
} | ||
|
||
getTypeFromCustomStruct( shaderStage ) { | ||
|
||
const uniforms = this.uniforms[ shaderStage ]; | ||
|
||
const bufferStructMap = new Map(); | ||
|
||
uniforms.forEach( ( uniform ) => { | ||
|
||
const { name, node } = uniform; | ||
const hasBufferStruct = node.bufferStruct === true; | ||
bufferStructMap.set( name, hasBufferStruct ); | ||
|
||
} ); | ||
|
||
return bufferStructMap; | ||
|
||
} | ||
|
||
getMembersFromCustomStruct( members ) { | ||
|
||
const structMembers = members.map( ( { name, type, isAtomic } ) => { | ||
|
||
let finalType = wgslTypeLib[ type ]; | ||
|
||
if ( ! finalType ) { | ||
|
||
console.warn( `Unrecognized type: ${type}` ); | ||
finalType = 'vec4<f32>'; | ||
|
||
} | ||
|
||
return `${name}: ${isAtomic ? `atomic<${finalType}>` : finalType}`; | ||
|
||
} ); | ||
|
||
return `\t${structMembers.join( ',\n\t' )}`; | ||
|
||
} | ||
|
||
getCustomStructNameFromShader( source ) { | ||
|
||
const functionRegex = /fn\s+\w+\s*\(([\s\S]*?)\)/g; // filter shader header | ||
const parameterRegex = /(\w+)\s*:\s*(ptr<\s*([\w]+),\s*(?:array<([\w<>]+)>|(\w+))[^>]*>|[\w<>,]+)/g; // filter parameters | ||
|
||
const results = []; | ||
|
||
let match; | ||
|
||
while ( ( match = functionRegex.exec( source ) ) !== null ) { | ||
|
||
const parameterString = match[ 1 ]; | ||
|
||
let paramMatch; | ||
|
||
while ( ( paramMatch = parameterRegex.exec( parameterString ) ) !== null ) { | ||
|
||
const [ fullMatch, name, fullType, ptrType, arrayType, directStructName ] = paramMatch; | ||
|
||
const structName = arrayType || directStructName || null; | ||
|
||
const type = ptrType || fullType; | ||
|
||
if (Object.values(wgslTypeLib).includes(structName) || structName === null) { | ||
|
||
continue; | ||
|
||
} | ||
|
||
results.push( { | ||
name, | ||
type, | ||
|
||
structName | ||
} ); | ||
|
||
} | ||
|
||
} | ||
Check failure on line 1043 in src/renderers/webgpu/nodes/WGSLNodeBuilder.js GitHub Actions / Lint testing
Check failure on line 1043 in src/renderers/webgpu/nodes/WGSLNodeBuilder.js GitHub Actions / Lint testing
Check failure on line 1043 in src/renderers/webgpu/nodes/WGSLNodeBuilder.js GitHub Actions / Lint testing
Check failure on line 1043 in src/renderers/webgpu/nodes/WGSLNodeBuilder.js GitHub Actions / Lint testing
Check failure on line 1043 in src/renderers/webgpu/nodes/WGSLNodeBuilder.js GitHub Actions / Lint testing
|
||
|
||
return results; | ||
|
||
} | ||
|
||
getStructMembers( struct ) { | ||
|
||
const snippets = []; | ||
|
@@ -1171,12 +1253,14 @@ | |
const bufferType = this.getType( bufferNode.bufferType ); | ||
const bufferCount = bufferNode.bufferCount; | ||
|
||
|
||
const isArray = bufferNode.value.array.length !== bufferNode.value.itemSize; | ||
const bufferCountSnippet = bufferCount > 0 && uniform.type === 'buffer' ? ', ' + bufferCount : ''; | ||
const bufferTypeSnippet = bufferNode.isAtomic ? `atomic<${bufferType}>` : `${bufferType}`; | ||
const bufferSnippet = `\t${ uniform.name } : array< ${ bufferTypeSnippet }${ bufferCountSnippet } >\n`; | ||
const bufferSnippet = bufferNode.bufferStruct ? this.getMembersFromCustomStruct( bufferType ) : `\t${ uniform.name } : array< ${ bufferTypeSnippet }${ bufferCountSnippet } >\n`; | ||
const bufferAccessMode = bufferNode.isStorageBufferNode ? `storage, ${ this.getStorageAccess( bufferNode ) }` : 'uniform'; | ||
|
||
bufferSnippets.push( this._getWGSLStructBinding( 'NodeBuffer_' + bufferNode.id, bufferSnippet, bufferAccessMode, uniformIndexes.binding ++, uniformIndexes.group ) ); | ||
bufferSnippets.push( this._getWGSLStructBinding( bufferNode.bufferStruct, isArray, 'NodeBuffer_' + bufferNode.id, bufferSnippet, bufferAccessMode, uniformIndexes.binding ++, uniformIndexes.group ) ); | ||
|
||
} else { | ||
|
||
|
@@ -1199,7 +1283,7 @@ | |
|
||
const group = uniformGroups[ name ]; | ||
|
||
structSnippets.push( this._getWGSLStructBinding( name, group.snippets.join( ',\n' ), 'uniform', group.index, group.id ) ); | ||
structSnippets.push( this._getWGSLStructBinding( false, false, name, group.snippets.join( ',\n' ), 'uniform', group.index, group.id ) ); | ||
|
||
} | ||
|
||
|
@@ -1228,9 +1312,69 @@ | |
stageData.codes = this.getCodes( shaderStage ); | ||
stageData.directives = this.getDirectives( shaderStage ); | ||
stageData.scopedArrays = this.getScopedArrays( shaderStage ); | ||
stageData.isBufferStruct = this.getTypeFromCustomStruct( shaderStage ); | ||
stageData.customStructNames = this.getCustomStructNameFromShader( stageData.codes ); | ||
|
||
// | ||
|
||
const reduceFlow = ( flow ) => { | ||
|
||
return flow.replace( /&(\w+)\.(\w+)/g, ( match, bufferName, uniformName ) => | ||
|
||
stageData.isBufferStruct.get( uniformName ) === true ? `&${bufferName}` : match | ||
|
||
); | ||
|
||
}; | ||
|
||
const extractPointerNames = ( source ) => { | ||
|
||
const match = source.match( /\(([^)]+)\)/ ); | ||
if ( ! match ) return []; | ||
|
||
const content = match[ 1 ]; | ||
|
||
return content | ||
.split( /\s*,\s*/ ) | ||
.map( part => part.trim() ) | ||
.filter( part => part.includes( '&' ) ) | ||
.map( part => part.replace( '&', '' ) ) | ||
.filter( part => ! part.includes( '.' ) ); | ||
|
||
}; | ||
|
||
const createStructNameMapping = ( nodeBuffers, structs ) => { | ||
|
||
const resultMap = new Map(); | ||
|
||
for (let i = 0; i < nodeBuffers.length; i++) { | ||
|
||
const bufferName = nodeBuffers[i]; | ||
const struct = structs[i]; | ||
|
||
resultMap.set(bufferName, struct.structName); | ||
|
||
|
||
} | ||
|
||
return resultMap; | ||
|
||
}; | ||
|
||
const replaceStructNamesInUniforms = ( shaderCode, map ) => { | ||
|
||
Check failure on line 1364 in src/renderers/webgpu/nodes/WGSLNodeBuilder.js GitHub Actions / Lint testing
Check failure on line 1364 in src/renderers/webgpu/nodes/WGSLNodeBuilder.js GitHub Actions / Lint testing
|
||
for ( const [ key, value ] of map.entries() ) { | ||
|
||
const regex = new RegExp( `\\b${key}Struct\\b`, 'g' ); | ||
shaderCode = shaderCode.replace( regex, value ); | ||
|
||
} | ||
|
||
return shaderCode; | ||
|
||
}; | ||
|
||
|
||
let pointerNames, structnameMapping; | ||
let flow = '// code\n\n'; | ||
flow += this.flowCode[ shaderStage ]; | ||
|
||
|
@@ -1255,6 +1399,68 @@ | |
|
||
flow += `${ flowSlotData.code }\n\t`; | ||
|
||
|
||
/* | ||
REVIEW COMMENT remove after review | ||
|
||
before reduceFlow: | ||
compute( &NodeBuffer_554.nodeUniform0, &NodeBuffer_558.nodeUniform1, &NodeBuffer_555.nodeUniform2, &NodeBuffer_556.nodeUniform3, &NodeBuffer_557.nodeUniform4, &NodeBuffer_559.nodeUniform5, instanceIndex, object.nodeUniform6 ); | ||
|
||
after reduceFlow: reduceFlow checks whether there is a storageStruct and if so then the | ||
postfix is removed so that the pointer points to the struct and not to a content in the struct | ||
compute( &NodeBuffer_554, &NodeBuffer_558, &NodeBuffer_555.nodeUniform2, &NodeBuffer_556.nodeUniform3, &NodeBuffer_557.nodeUniform4, &NodeBuffer_559.nodeUniform5, instanceIndex, object.nodeUniform6 ); | ||
|
||
extractPointerNames reads the names of the reduced pointers and stores them in an array | ||
Array(2) | ||
0: "NodeBuffer_554" | ||
1: "NodeBuffer_558" | ||
|
||
getCustomStructNameFromShader at the beginning reads the structNames from the shader header | ||
Array(2) | ||
0: {name: 'drawBuffer', type: 'storage', structName: 'DrawBuffer'} | ||
1: {name: 'meshletInfo', type: 'storage', structName: 'MeshletInfo'} | ||
|
||
|
||
createStructNameMapping links the automatic generated WGSLNodeBuilder for each struct with the struct name specified by the user. | ||
This is necessary because in wgslFn the user can choose any name in the shader in ptr for structs. | ||
|
||
Map(2) | ||
[[Entries]] | ||
0: {"NodeBuffer_554" => "DrawBuffer"} | ||
1: {"NodeBuffer_558" => "MeshletInfo"} | ||
|
||
replaceStructNames then replaces the names in the uniforms in the custom structs that the WGSLNodeBuilder | ||
created with the name chosen by the user. | ||
|
||
before replaceStructNames: | ||
|
||
struct NodeBuffer_554Struct { | ||
vertexCount: u32, | ||
instanceCount: u32, | ||
firstVertex: u32, | ||
firstInstance: u32 | ||
}; | ||
@binding( 0 ) @group( 0 ) | ||
var<storage, read_write> NodeBuffer_554 : NodeBuffer_554Struct; | ||
|
||
after replaceStructNames: | ||
|
||
struct DrawBuffer { | ||
vertexCount: u32, | ||
instanceCount: u32, | ||
firstVertex: u32, | ||
firstInstance: u32 | ||
}; | ||
@binding( 0 ) @group( 0 ) | ||
var<storage, read_write> NodeBuffer_554 : DrawBuffer; | ||
*/ | ||
|
||
|
||
flow = reduceFlow( flow ); | ||
pointerNames = extractPointerNames(flow); | ||
structnameMapping = createStructNameMapping(pointerNames, stageData.customStructNames); | ||
stageData.uniforms = replaceStructNamesInUniforms(stageData.uniforms, structnameMapping); | ||
|
||
if ( node === mainNode && shaderStage !== 'compute' ) { | ||
|
||
flow += '// result\n\n\t'; | ||
|
@@ -1289,6 +1495,11 @@ | |
|
||
} | ||
|
||
flow = reduceFlow( flow ); | ||
pointerNames = extractPointerNames(flow); | ||
structnameMapping = createStructNameMapping(pointerNames, stageData.customStructNames); | ||
stageData.uniforms = replaceStructNamesInUniforms(stageData.uniforms, structnameMapping); | ||
|
||
} | ||
|
||
} | ||
|
@@ -1493,15 +1704,27 @@ | |
|
||
} | ||
|
||
_getWGSLStructBinding( name, vars, access, binding = 0, group = 0 ) { | ||
_getWGSLStructBinding( isBufferStruct, isArray, name, vars, access, binding = 0, group = 0 ) { | ||
|
||
const structName = name + 'Struct'; | ||
const structSnippet = this._getWGSLStruct( structName, vars ); | ||
|
||
return `${structSnippet} | ||
if ( ! isBufferStruct ) { | ||
|
||
return `${structSnippet} | ||
@binding( ${binding} ) @group( ${group} ) | ||
var<${access}> ${name} : ${structName};`; | ||
|
||
} else { | ||
|
||
const StructName = isArray ? `array<${structName}>` : structName; | ||
|
||
return `${structSnippet} | ||
@binding( ${binding} ) @group( ${group} ) | ||
var<${access}> ${name} : ${StructName};`; | ||
|
||
} | ||
|
||
} | ||
|
||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's strange to me to find regular expression here. This shouldn't happen when code is generated from nodes, and not the other way around or reprocessed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally had the parser in mind that you also use for the pointers because it is part of the WGSLNodeBuilder. However, the parser does not recognize
stageData.codes
as WGSL code, so it always thrown an error. At first I thought that this would only be the case with compute shaders because the-> void
expression instageData.codes
is missing._getWGSLComputeCode
doesn't have that. But even when trying to use vertex and fragment shaders, the parser slipped into the else expression because it didn't recognize the shaders fromstageData.codes
as WGSL shaders.So far
stageData.codes
is not used anywhere. It was only created. In your opinion, does the code fromstageData.codes
have to be compatible with the parser?I then concentrated on reading out only the header, i.e. the part in the ( ), since the rest is not important. That seemed to me to be the safest and most stable way. If you see a better way to extract the datas from the shaders I welcome your idea. I originally wanted to forego the function entirely and do it with the parser.
Is the process clear from the review text?