Gaussian Splatting + PlayCanvas

Hi @slimbuck

PLY compression is indeed very impressive and makes the splat format somehow redundant.

However, Polycam stores splats in .splat format like:
https://storage.polycam.io/captures/59a5b823-2076-4eb0-a795-0dd6fa0910fd/raw.splat

When a user enters the poly cam link like (https://poly.cam/tools/gaussian-splatting?capture=59a5b823-2076-4eb0-a795-0dd6fa0910fd) it easy to just convert it to the raw URL and load the splats directly.

Also famous splats from the beginning are stored in .splat [hugginface link]

We load splat files in our [ own splat renderer ], but because yours is much more stable we want to switch. If you not planning to load splat files we need to implement a converter. But since you are already export splat files, I don’t want to write stuff you might have already on the roadmap (again :wink: )

PS: The splat format also makes it very easy to stream the data. I saw some streaming with PLY as well but not that efficient.

Thanks,
Tom

1 Like

Hi,

I just ported splaTV from animatter (Animated Gaussian Splatting) to Playcanvas (dirty gl access again).

Project:
https://playcanvas.com/project/1200526/overview/gaussiansplattv

Cheers,
Tom

4 Likes

Wow, that’s seriously cool! Nice job. Wondering whether it might make sense to uncheck Device Pixel Ratio for this demo - pixel fill is going to be pretty expensive for scenes like that.

1 Like

Works great. We just need to some nice content now!

1 Like

@slimbuck the PlayCanvas Gaussian splatting is so great! It is the fastest web implementation I have seen. Especially on macOS, its FPS is almost 3x~4x that of other WebGL implementations (such as Three.js or GSplat.js). I wonder how this is achieved?

I have gone through the code, and the major workflow looks similar. One of the core differences with others is that PlayCanvas uses fewer instances (128 splats for each instance). However, when I adjust the number from 128 to 1, meaning the full number of instances are used, the FPS only decreases slightly, less than 5%.

I am using non-compressed PLY data, and the FPS is calculated when the camera is static (meaning the sorting is not updating). I have also turned off antialiasing, and use max pixel ratio for all other implementations. I am really curious about what other huge optimizations you geniuses are doing to make it so fast.

Thank you!

Hi @xiasun,

I believe the big win you’re seeing is reordering the data so that splats nearby to each other in world space are nearby in memory also. This results in significant memory cache speedups on GPU at render time thanks to memory caching. (It’s actually a little strange just how random the splats appear in PLY files generated from training).

So we reorder splats in morton order at load time (see engine/src/scene/gsplat/gsplat-data.js at main ¡ playcanvas/engine ¡ GitHub).

Thanks!

1 Like

Oh, this is an amazing job. I attempted to return directly in reorderData(), and the FPS dropped from 23 to 18.

When I compared it with other WebGL implementations on MacOS, they only achieved FPS around 6. It seems there is still something more to the reordering process. :rofl:

The other thing we do is drop tiny splats, see this code.

If this is not the culprit, then my best guess would be that our magical fairy dust gives us the edge :slight_smile:

1 Like

:rofl: the early drops are also included in other impls. I’ll try to dig more. Thanks!

Also make sure there isn’t a difference in multisampling between the engines. This would definitely change perf, especially for splats.

1 Like

Finally I found that the result is possibly due to a bug in Chromium on Intel MacBooks. For Chrome on the Retina screen of Intel MacBooks, rendering directly to the canvas is slower than rendering to a texture. It seems to resemble a multisample/antialiasing-related misuse, but after numerous experiments, I believe it is an internal chromium bug and have submitted an issue.

PlayCanvas always renders to a render target (RT), so its performance is better in my case.

2 Likes

PlayCanvas does not always render to a render target. If multisampling is disabled, we render directly to the default framebuffer. (unless the users set up to render to a RT, either manually, or by using post-effects or similar).

But in the simplest case in the Editor, when using GS you typically disable multi-sampling, and it would render directly to the default framebuffer.

1 Like

Oh my bad, I meant in the SuperSplat project, the GS are by default rendered to framebuffer and then to the canvas.

For example, in my environment, I captured GL commands with Spector.js. I observed that drawElementsInstanced (the time-comsuming part) was drawn to “framebuffer 1” and then drawn to the “canvas framebuffer” as a quad.

ah right, yeah SuperSplat does that I think.