Performance differences with POINTS between 1.11 and 2.0

I have some questions about performance differences for POINTS shapes and related POINTS questions.

Compared to 1.11, the below code is much slower in p5.js 2.0. The goal is to draw 50K points with WEBGL. (I am drawing them in a spiral, this is irrelevant, I just need to be able to draw many points with WEBGL). Version 1.11 gives 60fps in my Mac with an M4 chip. Version 2.0 is well below 10.

Why the big difference? I suspect there is something different about how 2.0 handles points and that I should be writing this code differently. But how?

p5.disableFriendlyErrors = true;

function setup() {
  createCanvas(600, 600, WEBGL);

  // strokeCap(SQUARE);
  strokeWeight(2);
}

function draw() {
  background(128);

  orbitControl();

  N = 50000;
  beginShape(POINTS);
  for (i = 0; i < N; i++) {
    theta = (i / N) * 10 * PI;
    vertex(150 * sin(theta), 150 * cos(theta), i / 500);
  }
  endShape();

  if (frameCount % 100 == 0) {
    console.log(frameRate());
  }
}

Is there 2.0 reference documentation available somewhere? The documentation at Reference is for 1.11. I know 2.0 is still being developed, but is is the reference documentation for that available somewhere?

In p5.js the points are always round. In Processing (Java) I can change the point style to squares using strokeCap(SQUARE) but that method does not have the same effect in p5.js. In Processing all points are actually tessellated geometry, so a round point uses many more triangles than a square point, which only uses two. In Processing there is a huge performance advantage to using square points. Is something like that happening here with 2.0?

3 Likes

I don’t have the answer to your question, so I apologize if this is going off on a tangent.

The most efficient way to renders large amounts of geometry is to bypass p5 or Processing and go direct to webgl / opengl using shaders. As an example, https://infinitefunspace.com/p5/ball/ animates 100,000 spheres without storing or passing any data between the CPU and GPU. Each sphere is ray-traced by the fragment shader onto a single triangle whose position and size are computed deterministically in the vertex shader based on the gl_VertexID. You can see the source at https://infinitefunspace.com/p5/ball/p5Ball.js.

If you need to position the geometry explicitly computed on the CPU, typically in opengl/webgl, you would pass positions to the GPU using vertex buffer objects which are a bit of pain to set up and can conflict with p5 or Processing. An alternative is to pass data to the GPU through textures. p5 makes this a bit easier using floating-point framebuffers. I have several p5 examples at https://openprocessing.org/user/465377?view=sketches that also use shaders to compute the positions of the particles.

2 Likes

The 2.0 reference is available at beta.p5js.org!

I’m not sure specifically whats slowing down your case here, but points currently are drawn differently than strokes. If you use LINES shape mode and add a second point 0.001 away from the original in some axis, do you see any performance difference?

In p5 stroke caps and join styles are all done in the fragment shader so there should be negligible performance difference between them – a round cap is still rendered with just a quad geometry. But currently anyway there is a separate point shader, so trying with LINES mode will use a different rendering pipeline, which is worth testing.

3 Likes

No, don’t apologize, your reply is super helpful. Your examples suggest a completely different approach that I hadn’t considered because I don’t know enough about it. I read through the code for the 100,000 spheres and your p5 examples. For my situation I’ll probably need to explicitly compute the geometry on the CPU and pass the positions with the float point framebuffers. I need to experiment with this to learn more about it. I think it might work for what I need to accomplish.

I’m also curious if I can write code in Processing to do similar work with frame buffers? Not through the API, but through the PGL class through with Processing interacts with OpenGL.

I tried this and found that it was very slow in both 1.11 and 2.0.

Excellent, thank you! I will make good use of this.

OK, I see. This is better than how Processing handles this. Processing draws lots of triangles for the joints, and that makes it harder to write custom shaders for lines.

1 Like

Quite unfortunately, Processing does not (yet?) support floating-point textures or framebuffers. I submitted a feature request for them to github just before @davepagurek announced them in p5, but as far as I know, no one has worked on it for Processing. And since Processing doesn’t use floating-point textures, PGL has no support either.

You could try to write the direct OpenGL yourself to create floating-point textures through JOGL, which I have not tried, but that has a tendency to tangle with Processing’s own internal texture management. Instead, I have been using SSBOs which let you pass general float or integer values to and from the GPU. Your problem there, however, is that they require OpenGL 4.3 which came out after Apple took their toys and went home to create their own graphics library, Metal, and stopped supporting versions after 4.1.

Another possibility is, in the shader, to interpret the 4x8-bit texture colors as integers letting you pass in one or more coordinates per pixel. You could either use a fixed point representation or use the GLSL function intBitsToFloat() to convert to a floating-point value along with Java’s Float.floatToIntBits().

Or just bite the bullet and use the full OpenGL vertex array buffers. I shared an example rendering a million animating points here: https://discourse.processing.org/t/cant-render-opengl-mesh-and-p3d-components-at-the-same-time/44624/2. If you want circle points instead of the default squares you can add the line

  if( length(gl_PointCoord.xy-0.5)>0.5 ) { discard; }

to the fragment shader.