Scthe / nanite-webgpu

UE5's Nanite implementation using WebGPU. Includes the meshlet LOD hierarchy, software rasterizer and billboard impostors. Culling on both per-instance and per-meshlet basis.
https://scthe.github.io/nanite-webgpu/?scene_file=jinxCombined&impostors_threshold=4000&softwarerasterizer_threshold=1360&nanite_errorthreshold=0.1
MIT License
649 stars 12 forks source link

"Fill size not multiple of 4" error at some window sizes #2

Open hrydgard opened 1 week ago

hrydgard commented 1 week ago

Very cool stuff. However, sometimes getting this in Chrome / windows in the demos:

image

Resizing the window a bit usually fixes it, so I think maybe you are filling bytes but specifying pixel count?

Scthe commented 1 week ago

Should be fixed in https://github.com/Scthe/nanite-webgpu/commit/a562d80df3da237034bee62d97d87bce0db04967 . While I was not able to reproduce this, I had this error in the past. It's a regression introduced in https://github.com/Scthe/nanite-webgpu/commit/20f768a97df2bcbde7c9bbe02107727c0407d9c9 .

In WebGPU spec, clearBuffer() has offset and size parameter as optional. Both MDN and Chrome agree. Firefox's wgpu does not. It always requires 3 params.

But there is something more interesting here. On your screenshot, the size parameter is 12347125. The buffer was created with size: BYTES_U32 * viewportSize.width * viewportSize.height. The value for BYTES_U32 is 4 - a number of bytes in an 32-bit unsigned int. Viewport dimensions should be positive integers. How does 4 * viewportSize.width * viewportSize.height produce a number that ends with 5? I don't even want to investigate.

I'm using Windows with standard cheap monitors and was still able to get this bug. No crazy retina display, weird DPI or whatever (which usually handled by devicePixelRatio, etc anyway). So the viewport size should be an integer.

dzaima commented 1 week ago

In the devtools console (on linux) I'm seeing:

Viewport resize {width: 1433.6875, height: 654.3125}

for an error of "Fill size (3752318) is not a multiple of 4 bytes.", which matches a floored 4*1433.6875*654.3125. So width/height need some rounding somewhere.

Scthe commented 1 week ago

(Hopefully) fixed in https://github.com/Scthe/nanite-webgpu/commit/64b5471e39c91604fa81dd098ec296a2debd17fe.

Ok, I see where the problem is:

width: canvas.clientWidth * devicePixelRatio,
height: canvas.clientHeight * devicePixelRatio,

devicePixelRatio is used to handle screen DPI (Retina display, etc.). It does not have to be an integer. If you ctrl + scroll wheel to zoom on a page, it will be fractional. The fix is to Math.ceil() the viewport dimensions. There is a better way to handle changing devicePixelRatio, but for now I'm more interested in fixing this.

Thanks everyone who spend their time testing and debugging this issue!

Repro:

  1. ctrl + scroll wheel to zoom on the page, which sets devicePixelRatio that is not an integer.
  2. Refresh the page.