Skip to content

WebGPU/MSAA/HDR correctness fixes for the render passes#7

Open
Ludv1gL wants to merge 1 commit into
rlamarche:mainfrom
Arvikasoft:webgpu-msaa-fixes
Open

WebGPU/MSAA/HDR correctness fixes for the render passes#7
Ludv1gL wants to merge 1 commit into
rlamarche:mainfrom
Arvikasoft:webgpu-msaa-fixes

Conversation

@Ludv1gL

@Ludv1gL Ludv1gL commented Jul 2, 2026

Copy link
Copy Markdown

Hi! We've been running bevy_pointcloud in production in the browser (WebGPU backend, MSAA + HDR cameras) as the point renderer behind bevy_3d_tiles, and hit a cluster of validation failures that native Vulkan tolerates but WebGPU core does not. This PR upstreams the fixes we've been carrying — thank you for the crate, it's been excellent to build on.

The fixes

  1. f16 intermediates in the depth/attribute passes (Rg16Float/Rgba16Float): WebGPU rejects float32 color formats for multisampling and blending, so the EDL path failed validation whenever MSAA > 1. f16 keeps the additive merge behavior with plenty of precision for depth-in-view + attribute accumulation.
  2. Sample-count matching: those passes attach the view's depth texture, so their color targets/pipelines now specialize on the view's MSAA sample count instead of assuming 1.
  3. Normalize pass HDR specialization: it writes to the camera's view target, which is Rgba16Float for HDR views (e.g. cameras with bevy_pbr::Atmosphere); the hardcoded bevy_default() format failed validation there. The pipeline key now carries hdr.
  4. Atmosphere mesh-view layout: views with bevy_pbr::Atmosphere use the atmosphere variant of the mesh-view bind group layout — the depth/attribute pipeline layouts now specialize to match (wgpu rejects the draw otherwise).
  5. Screen-space minimum point size in the simple path (point_cloud.wgsl): the non-octree branch sized points purely in view space, so distant points dropped below one raster sample and vanished (with moiré on the way down). This mirrors the existing clamp in the IS_OCTREE branch, using the same min/max_point_size pixels.

All five are battle-tested — they've been running in our production browser viewer since mid-June, streaming multi-million-point clouds. cargo check is green with default features and with webgpu.

One more thing

Because the crate is git-only (the potree/copc features carry git dependencies, which crates.io forbids), we currently publish a temporary fork on crates.io (bevy_pointcloud_x) purely as a publishing vehicle so our own published crate can depend on it — its README points people back here. We'd love to retire it. If you're open to publishing bevy_pointcloud to crates.io (e.g. potree/copc behind non-default features that move to git-dep-free releases, or a separate companion crate), we're happy to help with the restructuring, CI, or anything else that makes it easy. Either way, thanks again for the crate!

🤖 Generated with Claude Code

Found running the crate in the browser (WebGPU backend) with MSAA and
HDR cameras — native Vulkan tolerates all of these, WebGPU core does not:

- depth/attribute passes: f16 intermediates (Rg16Float / Rgba16Float).
  WebGPU rejects float32 color formats for multisampling and blending,
  so the EDL path failed validation with MSAA > 1.
- depth/attribute pass textures + pipelines now match the view's MSAA
  sample count (they attach the view's depth texture, so their color
  targets must agree with its sample count).
- normalize pass: specialize the color target on the view's HDR-ness.
  It writes to the camera's view target — Rgba16Float for HDR views —
  and the hardcoded bevy_default() failed wgpu validation there.
- depth/attribute pipeline layouts specialize for views carrying
  bevy_pbr's Atmosphere (atmosphere mesh-view bind group layout variant;
  without it wgpu rejects the draw on such cameras).
- point_cloud.wgsl: clamp the simple (non-octree) path to a minimum
  screen-space point size, mirroring the IS_OCTREE branch — distant
  points dropped below one raster sample and vanished (with shimmer on
  the way down).
@rlamarche

rlamarche commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Hi,

Thanks a lot for your contribution ! And I'm very happy that my crate (not yet because not published yet 😅) is already used in production. It's a shame that I wasn't the first 🙂.

I've reviewed briefly your changes, and they are indeed all very relevant.

The point that surprises me the most, is the WebGPU compatibility. I was convinced it should work, but dit not test. I thought that, because if it was working on WebGL, it should work on WebGPU. This limitation of f32 textures is surprising. But indeed, we don't really need this precision, as you said.

By the way, I've some other news, that I believe you will enjoy despite the work you've done.

I started a complete rewrite of my crate last week. Indeed, trying to add custom materials, and custom attributes and so on, I realized that I had over-engineered a little (a lot !) too much with the overkill usage of generics, and my project was becoming out of control (see customizations branch).

Rust is a strongly typed language, but it doesn't means we shouldn't use type erasing where necessary. I was already doing if for the loaders, and I finally decided to generalize that.

More over, I was doing a lot of work with buffer allocations, and finally found that the Bevy's MeshAllocator is a piece perfectly engineered that I could use for my own benefits.

Finally, I was using my own slab structures, because I wrongly thought that the performances would be better than instancing entities ... I'm almost sure I was wrong. Bevy's ECS performs very well with a lot of entities (as I've read on the discord channels), so the rewrite make use of theses entities to reflect the chunks hierarchy (only the visible chunks and loaded chunks). There is also an asset per loaded chunk, and the Mesh3d entity is also reused to not reimplement the extracting of meshes.

It has the effect of simplifying a lot the extract, prepare and render code, and allowed to reuse the ViewBinnedRenderPhases ressource, among others.

The rewrite focus on :

  • more cleaner PI
  • less rewrites
  • more customizations
  • more simplicity

And it pays !

I've still yet to implement the multi-pass rendering, but except that, I have already more rendering features, and fully customizable rendering pipelines.

I'm currently writing a lot of documentations, also to help me implementing the missing features, because I don't work on a daily basis on this project, and I forget easily the complex parts.

You can find this new version here : https://github.com/rlamarche/bevy_pointcloud/tree/reboot alongside with its documentation. The book is not published yet, but it contains useful diagrams for better understanding.

I hope to implement the multi pass pipelines, and finalizing the support for octrees, and maybe finally publish a version on crates, by the end of the week, but nothing guaranteed at this time.

The crate might be named bevy_point_cloud to follow crates conventions. If so, I would rename this github repo

@Ludv1gL

Ludv1gL commented Jul 3, 2026

Copy link
Copy Markdown
Author

Hi and Thank you for your prompt reply. Hehe, production might be stretching it a bit, I had Claude draw up the reply and it got a bit too enthusiastic. But yes, great news on the rewrite. We are soon-ish migrating to 0.19 which sounds like a good time to switch to your new crate, and delete our fork crate. Let us know!

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.

2 participants