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

[WebGPU] drawIndirect and drawIndexedIndirect #28389

Open
AIFanatic opened this issue May 15, 2024 · 6 comments
Open

[WebGPU] drawIndirect and drawIndexedIndirect #28389

AIFanatic opened this issue May 15, 2024 · 6 comments
Assignees
Labels
Milestone

Comments

@AIFanatic
Copy link

Description

Hi, is there any plans to support drawIndirect and drawIndexedIndirect? I have searched the issues and the code base and could not find any references to either.

Solution

Not entirely sure what the best approach would be here but maybe provide a renderer.renderIndirect method that allows an array/buffer reference to be passed? I guess the WebGL backend would have to fallback to drawElements or equivalent.

Alternatives

I have implemented a nanite equivalent in three.js and the bottleneck now is the lod test since its done on the cpu. The algorithm is perfect for the gpu and almost everything could be implemented in WebGL but it would always require a gpu->cpu->gpu roundtrip to read how many meshlets should be displayed in order to properly call drawElements.

Additional context

No response

@CodyJasonBennett
Copy link
Contributor

CodyJasonBennett commented May 15, 2024

I've been implementing that exactly, and you may take an interest to #28103 in lieu of indirect draw or some of my GPU driven experiments with meshlets and culling/visibility tests particularly https://twitter.com/Cody_J_Bennett/status/1736555185541886407, https://twitter.com/Cody_J_Bennett/status/1730911419707842973.

@AIFanatic
Copy link
Author

Hi @CodyJasonBennett I have came across your gpu-culling code before and if im understanding it only discards the fragment shader by setting the w value to 0 if not visible in the vertex shader. This is my issue with current solutions, same with BatchedMesh.
I have done some tests and discarding geometry in the vertex shader gives some performance boost but not a lot, specially considering that most meshlets won't be rendered. Also would be great to discard things on a geometry level and not on a geometry vertex level, if a whole meshlet is not visible I would like to just discard the whole thing in one go instead of discarding each vertex, wasting gpu.

With drawIndirect we can do something like (each is a compute pass):

  1. With meshlets AABB's and indices perform visibility culling
  2. Prefix sum
  3. Reindex
  4. Count visible meshlets
  5. Create indirect buffer based on above

After all done drawIndirect can be used with the GPU data to directly render the visible meshlets only, no need to go over invisible ones, also no gpu->cpu transfer is needed. Keep in mind that rendering the meshlets is not the issue, I have implemented something akin of an InstancedMesh for meshlets that can draw many instances in one draw call. The visibility culling is the problem, in non web environments this is easily solvable by using mesh shaders.

Offtopic but big fan of four, planning on using it as a base for what im trying to do above.

@CodyJasonBennett
Copy link
Contributor

CodyJasonBennett commented May 15, 2024

FWIW those experiments were as close as I could get to GPU driven rendering which is comparably trivial in WebGPU. The use of transform feedback performs culling at the instance or batch level and then the vertex shader will short circuit if the resulting instance buffer is zeroed. Doesn't work so great for virtualized geometry where you have a fixed number of vertices or vertex buffer memory; you want to move data and this can only be done with compute or carefully vectorized CPU code via WASM with GPU driver overhead from upload (introduces latency). May be the easiest path for you in the near term since you're already using METIS and Meshoptimizer.

On the topic of indirect drawing in WebGPU, or more specifically the WebGL compatibility side, my knee-jerk thought is to fallback to multi-draw (hence the backlink to my PR), but the data is different. WebGPU expects an interleaved buffer with draw arguments, but multi-draw expects separate buffers per argument, and they have to be consumed on the CPU. It may be best to consider this feature WebGPU only and consider compatibility only when proven feasible (cc @RenaudRohlinger, curious of your opinion here). This is one of the flagship features of WebGPU people migrate specifically for, alongside GPU shared memory, atomics, multisampled textures, etc. There is no WebGL 2 equivalent, even if it can be ported in a strictly worse fashion.

@RenaudRohlinger
Copy link
Collaborator

Interesting topic here! drawIndirect is also part of my roadmap for achieving a new type of 'static' scene that I have in mind for threejs. I have started working on it here, with the support of the CAD software Plasticity.

Here is how I envision my roadmap:

  • Add RenderBundle support with this pull request.
  • Implement a single buffer pass with the pull request of @aardgoose RFC: WebGPURenderer prototype single uniform buffer update / pass #27388 to batch all the writeBuffer commands in one command, already bundled by a RenderBundle.
  • Add a global UBO pipeline (UBO for the camera and scene elements such as fog, etc.) to update only a single UBO per frame, (instead of pre-calculating the modelViewMatrix on the CPU per mesh per frame) and multiply camera matrices by model matrices directly on the GPU instead of on the CPU.
  • Add frustum culling support via indirect draw calls and compute shading as described in Toji's article Render Bundles and Object Culling.

Each feature on this roadmap should be cherry-pickable once shipped to three.js.
I have never studied Unreal Nanite technology, but I believe all these features combined would be what's needed for this type of GPU rendering

@CodyJasonBennett
Copy link
Contributor

I'm not sure how that relates to this issue. What's described here is indirect drawing which would benefit from an interface with compute. That seems like a backlog of issues which affect WebGPURenderer generally.

@Mugen87 Mugen87 added the WebGPU label May 16, 2024
@CodyJasonBennett
Copy link
Contributor

CodyJasonBennett commented May 16, 2024

I won't hijack this issue any further, but I'm happy to explore this topic in WebGPU and later WebGL 2 (with expected latency). Today, I would lean with WASM + multi-draw which could use #28103. This is an area I've incidentally been studying which was the motivation behind all the prior work I linked. Awesome progress and great to see I'm not alone on the web, Bevy aside.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants