-
-
Notifications
You must be signed in to change notification settings - Fork 36k
Struct array extension #30487
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
Struct array extension #30487
Conversation
📦 Bundle sizeFull ESM build, minified and gzipped.
🌳 Bundle size after tree-shakingMinimal build including a renderer, camera, empty scene, and dependencies.
|
@Spiri0 Could you show me some use case of the API code in the issue? I'm not sure I understand the example part. |
The simplest case will probably be where you combine position, normal, uv in an interleaved buffer, which is what I do in my virtual geometry system. Here is my somewhat simplified shader to see it better.
I no longer have a single classic attribute in my vertex shader. A compute shader decides which meshes should be drawn using drawIndirect and the ObjectInfo structArray data from the compute shader. I could have used 3 separate storages for position, normal, uv, which would then be vec3, vec3, vec2 arrays like you are used to. These 3 basic datas (position, normal, uv) can be easily combined in a structArray and sent to the GPU in a single storageBuffer. So StructArrays are pretty analogous to the basic property arrays, vec2, vec3, vec4, ... but with them you can bundle data much more efficiently instead of having to use a single storage for each individual basic property. In the picture on the right I have extended the WebGPU limits up to the maximum of my graphics card. So the maximum possible storageBuffer size that is possible. And yet the frame rate is still very high. Each colored area is a single mesh controlled with drawIndirect. This is basically an analog system to unreal's nanite system. You can't see a transition between the individual meshlets ![]() ![]() The visibility of the many meshlets can only be controlled using structArrays. That's why, like you, I put so much energy into the struct extension, because drawIndirect relies on structArrays to be used really effectively. Together and with the storages and a compute shader, they overcome the limits with traditional attributes. With these tools in threejs, the mesh count is no longer the limiting factor. In my app I also bundle all the camera datas that I need for drawIndirect in a single struct and storageBuffer and send them in each interval in one storage to the gpu. So I'm basically doing what @aardgoose intended to do with its expansion #27388 |
The |
Is that helpful ?
I had also used 8 instead of 12 and placed uv.x in the w component of the position and uv.y in the w component of the normal vector, but that is exaggerated storage efficiency. So I pack the data of each vertex from all mesh fragments into a storage. For the dragon, in my current config there are over 1300 individual meshlets. The Interleaved storageBuffer therefore contains all the data that would normally be distributed across the three attributes (position, normal, uv). In order to control which indexes belong to which mesh and which vertice in the mesh, several structArrays are necessary. One of my structs "meshletInfoStruct" is also a structArray and only appears in the compute shader like the DrawBuffer struct and is already a large struct with 48 elements per meshlet. With over 1300 meshlets that means over 1300 structs, each with 48 elements in a storage as an array. I've already toyed with the idea of putting the whole project as a repository on Github. Data compression with structArrays is very powerful. Attribute-based geometry will be around for a long time. But I strongly assume that the storageBuffers, which allow free access to every element very efficiently with drawIndirect, are the future. |
examples/webgpu_struct_array.html
Outdated
camera.position.set( 1, 1, 1 ); | ||
const controls = new OrbitControls( camera, renderer.domElement ); | ||
|
||
let computeDrawBuffer, computeInitDrawBuffer; |
Check notice
Code scanning / CodeQL
Unused variable, import, function or class
examples/webgpu_struct_array.html
Outdated
camera.position.set( 1, 1, 1 ); | ||
const controls = new OrbitControls( camera, renderer.domElement ); | ||
|
||
let computeDrawBuffer, computeInitDrawBuffer; |
Check notice
Code scanning / CodeQL
Unused variable, import, function or class
examples/webgpu_struct_array.html
Outdated
await renderer.init(); | ||
|
||
let m; | ||
const width = 1 |
Check notice
Code scanning / CodeQL
Semicolon insertion
@Spiri0 Thanks for the example, what do you think about having |
Hi @sunag, I'm currently struggling with something as banal as the screenshot. Since the new chrome this doesn't work for me. P.S. Thanks for the fix with the outputNode. It would have easily taken me a week to work my way through the code and find the issue. |
e3f1b69
to
426c39a
Compare
Related issue: #30394
This is a small extension of the new struct feature.
A storage can have millions of entries and in this storage there can not only be a single struct, but a huge amount of structs. These structs are then completely analogous to the many vec4 in a storage, only each struct contains a whole bundle of datas.
This is in my virtual geometry system the case where I have a lot of meshlets. Each meshlet is characterized by an extensive set of datas. However, the data sets of many meshlets can be bundled into just a few storages so that the large amount of data from the numerous meshlets of the GPU can be made available in a very compact manner in these few storages.
With this extension: The WGSLNodeBuilder recognizes from the length of the struct and the length of the storage array whether it is a single struct or a struct array