processing / p5.js

p5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is based on the core principles of Processing. http://twitter.com/p5xjs —
http://p5js.org/
GNU Lesser General Public License v2.1
21.67k stars 3.33k forks source link

[p5.js 2.0 RFC Proposal]: WebGL Outlines #7278

Open davepagurek opened 1 month ago

davepagurek commented 1 month ago

Increasing access

Lines in WebGL right now are catered towards line drawings rather than 3D shape outlines. Many older sketches that treat them more as outlines currently run significantly slower than they used to in earlier versions. Ideally, there would be a way to make that use case work again so that earlier teaching materials work as intended without also breaking the sketches than now use lines and rely on the current behaviour.

Which types of changes would be made?

Most appropriate sub-area of p5.js?

What's the problem?

WebGL lines currently are too slow to be drawing them in large numbers in immediate mode shapes. Many older teaching examples do this because earlier lines were lighter, and those are now much slower. The use case of 3D shape outlines also does not need the high fidelity caps and joins that are required for smooth curves.

This particular example from The Coding Train keeps coming up, as one that slows down a lot in newer p5 versions https://editor.p5js.org/codingtrain/sketches/OPYPc4ueq but it also does not need complex lines to accomplish its visual goal.

What's the solution?

There are a few options I can think of currently, all with different compromises:

In addition to the above, we can maybe speed up some of the existing line drawing via the things mentioned in https://github.com/processing/p5.js/issues/7237.

Pros (updated based on community comments)

Cons (updated based on community comments)

Proposal status

Under review

perminder-17 commented 1 month ago

Thanks for summarizing the solutions @davepagurek . Each approach solves the issue but leaves us with smaller challenges :"). Personally, I like the first and third solutions the most.

  1. The first solution, using GL_LINES, is straightforward, but I feel its limitations could be noticeable. On high-DPI displays, where the pixel density is higher, the line thickness might appear smaller relative to the canvas. So, while it’s a good option, I’m unsure if it’s the one I’d choose.
  2. The second solution is easy to implement, but I don’t find it very user-friendly. One point of confusion for me is whether we're giving users the option to add caps and joins, or if we’re removing them entirely. If we offer users these options, it could speed up the line drawing, but it also adds complexity by requiring them to specify it. If we’re just removing these features when drawing outlines, that's not good either. Could you clarify? Or maybe its kind of like for the outline purpose we are removing the joins and caps?
  3. The third solution seems like the best fit for me. There are several reasons for this:

    • It will fully utilize the GPU, reducing unnecessary CPU and GPU computations.
    • Refactoring the WebGL renderer to always use a main framebuffer will unlock future possibilities like depth-blur, fog, ambient occlusion (AO), and more filters.
    • We have seen that using a framebuffer is faster than a standard WEBGL canvas, especially with optimizations like imageLight() and filter shaders drawing into the framebuffers. It improved the performance so in future, we are using framebuffer could be faster as well.

The main challenge could be figuring out when to draw the main framebuffer onto the canvas to make it visible—probably at the end of the draw loop. (we can figure that out)

Being a filter shader, it would also apply to everything on canvas, so you couldn't control it and its color on a per-shape basis.

If that’s not a major concern, I’d prefer going with the third solution, even if it means a breaking change (internally in p5 system). It could open up a lot of possibilities for future improvements. Let me know what you think!

davepagurek commented 1 month ago

to clarify that second option: I was imagining the simpler lines mode being an option you can set (maybe at the start of your sketch?) but it may not be easy to have all the features of the current system be optional without introducing bugs, since there are a number of parts we'd have to disable to return to the earlier performance.

perminder-17 commented 1 month ago

too clarify that second option: I was imagining the simpler lines mode being an option you can set (maybe at the start of your sketch?) but it may not be easy to have all the features of the current system be optional without introducing bugs, since there are a number of parts we'd have to disable to return to the earlier performance.

Ooh... Got it. Thanks for the clarification 🙂

davepagurek commented 2 weeks ago

Actually, we might not need to do a huge refactor in order to support the earlier, low fidelity outlines. I did a little test here https://editor.p5js.org/davepagurek/sketches/jfNPJGoqV where setting testNoCapsOrJoins = true at the start comments out the calls to addCap and addJoin in _edgesToVertices without changing any of the other infrastructure. On my mac, it makes the perf of this sketch go from 20fps to 40fps on average. Rather than commenting these out, we could just wrap those sections in an if statement that checks whether the current webgl context uses simple lines or not, e.g. with strokesMode(SIMPLE) (vs a default strokesMode(FULL), for example.)

That might actually be the fastest way to accomplish the original goal of just making strokes faster for use cases that need them. It doesn't go quite as far as to reach the 60fps the original in p5 0.7.2 gets me, but maybe it's enough?

davepagurek commented 2 weeks ago

One more test case: the Coding Train sketch with noStroke() added, and using a filter shader to do the outlines. This gets me 60fps: https://editor.p5js.org/davepagurek/sketches/ZgKTL4Tmm