xyz-tools / gcode-preview

A simple GCode parser & previewer lib with 3D printing in mind. Written in Typescript.
https://gcode-preview.web.app/
MIT License
160 stars 31 forks source link

Use BatchedMesh to improve performance #164

Closed sophiedeziel closed 4 months ago

sophiedeziel commented 5 months ago

Part of #95

Using BatchedMesh to group all meshes of the same material to reduce the number of draw calls.

The initial render time is slightly higher, but not human noticeable. However, performance gains on each animation frame is significantly better. It's about 5x faster. See comments below. It makes a great difference when large models are rendered, especially when the viewer is embedded in a framework that's on the heavy side.

Changes:

github-actions[bot] commented 5 months ago

Visit the preview URL for this PR (updated for commit 796c36a):

https://gcode-preview--pr164-feature-batched-mesh-e3sllffe.web.app

(expires Sat, 20 Jul 2024 03:38:25 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: 59bd114ae4847b32c2bba0b68620b9069a3e3531

sophiedeziel commented 5 months ago

I was not looking at the right place for performance improvements!

Yes, the initial render is a bit slower. But to the human eye, it is not noticeable.

However, each animation frame gets an amazing improvement! I went back to check after I noticed a great slowdown of my web app, and heat up of my laptop. I had skipped frames and I wondered if this PR had render improvements that I did not notice.

Before

Screenshot 2024-06-06 at 22 26 34

After

Screenshot 2024-06-06 at 22 21 57

It does make a great difference when gcode-preview is embeded in a heavy framework.

This PR would replace https://github.com/remcoder/gcode-preview/pull/165 completely, since it's reducing the number of materials in a similar way.

sophiedeziel commented 5 months ago

Archiving the initial PR description because I'll rewrite it:

I'll start by saying that the results are disappointing at first.

It seems to have made the performance worse, but it may also be mis-use of the feature or a mistake in the code.

My theory is that batchMesh is good when adding meshes to the scene individually. But we're already using groups for that. What we gain by reducing the number of new materials is insignificant compared to the overhead of instanciating a batchedMesh and adding geometries to it.

From the docs:

Use BatchedMesh if you have to render a large number of objects with the same material but with different world transformations and geometry

I think there's no improvement because the geometries don't have different world transformations.

I'll take those learnings and try to apply the same logic of a color index, but to only instanciate one material per color. The improvement may not be noticeable, but we'll reduce the memory footprint.

sophiedeziel commented 5 months ago

Now, looking at a ~300ms window:

Before

Screenshot 2024-06-06 at 22 54 40

After

Screenshot 2024-06-06 at 22 55 12
remcoder commented 5 months ago

Very cool results! Would this improvement be mostly felt with larger models? If it can reduce the sluggishness with large models that would be super!

remcoder commented 5 months ago

I tested this with an 8MB large model and got an exception:

Uncaught (in promise) Error: BatchedMesh: Reserved space request exceeds the maximum buffer size.
    at BatchedMesh.addGeometry (three.module.js:33220:10)
    at Mt.batchGeometry (gcode-preview.es.js:1:47533)
    at gcode-preview.es.js:1:46092
    at Array.forEach (<anonymous>)
    at Mt.addTubeLine (gcode-preview.es.js:1:46015)
    at Mt.doRenderExtrusion (gcode-preview.es.js:1:43644)
    at Mt.renderLayer (gcode-preview.es.js:1:42984)
    at Mt.render (gcode-preview.es.js:1:41315)
    at HTMLCanvasElement.<anonymous> (demo.js:311:13)
sophiedeziel commented 5 months ago

There's probably something I need to learn about javascript exceptions!

https://github.com/remcoder/gcode-preview/pull/164/files#diff-8ec787c979ef6cb88e426e798a75cc41d326888eaca61c5f5691b18c5e65c729R706-R710

This is most likely where it is triggered, but I'm catching errors.

sophiedeziel commented 5 months ago

Very cool results! Would this improvement be mostly felt with larger models? If it can reduce the sluggishness with large models that would be super!

The larger the model, the most noticeable the improvement is!

remcoder commented 5 months ago

I notice it is much smoother (higher FPS) with batchedMesh!

However I consistently run into the above error with files over 4.5MB.

remcoder commented 5 months ago

I can fix it by raising maxVertexCount(second param) in new BatchedMesh(200, 10000, undefined, material);

sophiedeziel commented 5 months ago

I can fix it by raising maxVertexCount(second param) in new BatchedMesh(200, 10000, undefined, material);

Yes, ideally the count should be calculated to avoid over-allocating and to make sure it can support geometries of any size.

I'll try a refactor because I realized that this may not play very well with the progressive rendering. There would be some benefits to generate all geometries first and then add them to BatchedMesh instances in one go. There are memory tradeoffs that I want to measure too.

sophiedeziel commented 4 months ago

I wanted to test the limits:

Screenshot 2024-06-19 at 23 02 31

The initial render is slow, but the line one (non-tube) takes 2200ms.

It runs at 15 FPS, which is way smoother than expected! The develop branch can barely do 3 or 4 FPS.

This branch:

Screenshot 2024-06-19 at 23 06 53

Before:

Screenshot 2024-06-19 at 23 12 49
sophiedeziel commented 4 months ago

With the last commits that create fewer batchMeshes, the multicolor benchy's render frames are almost cut in half. Went from ~5.75ms to ~2.90 on average

Screenshot 2024-06-19 at 23 42 12