Why does frameRate go slow after exactly 100 get() calls?

If you make a get(x,y) call every frame in P5JS, after 100 frames the frameRate suddenly drops from 60fps (or whatever it was set to) to about 14fps (or even slower). This only happens if there are some image objects drawn to the screen. It does not happen if the screen is filled with graphic objects like ellipses etc. It also slows down if using loadPixels() and calls directly such as var r = pixels[p]

Why is this?

Does it have something to do with the following console warning :
<Canvas2D: Multiple readback operations using getImageData are faster with the willReadFrequently attribute set to true. > which I can never get rid of and have learned to live with.

Incidentally if you get the code running slow then click on another tab and then back to our tab - the code now runs at normal speed ! Which is a horrible fix.

Here is some skeleton code which demonstrates this slow down (provide your own png).

Thanks

var cnv;
var fps = 60;
var tileImage;

function preload(){
  tileImage = loadImage("data/tile0.png");
}

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

  frameRate(fps);
}


function draw() {
  //draw a grid of images
  background(0);
  for(var i=0;i<width;i+=50){
    for(var j=0;j<height;j+=50){
      image(tileImage,i,j,50,50);
    }
  }

  var col = get(mouseX, mouseY);

  fill(255);
  textSize(50);
  text("fps = " + round(frameRate()), 50, 50);
  text("col = " + col, 50, 100);
  text("frameCount = " + frameCount, 50, 150);
}

I’ve found a way to solve this which is better than switching between tabs. I don’t understand why it would be quicker but it is. If you do the drawing to a different PGraphics layer then copy that to the screen the get() or pixels functions stay fast. Here’s the code

var fps = 60;
var tileImage;
var bg;

function preload(){
  tileImage = loadImage("data/tile0.png");
}

function setup() {
  createCanvas(800, 600);
  bg = createGraphics(width, height)

  frameRate(fps);
}

function draw() {
  bg.background(0);
  for(var i=0;i<width;i+=50){
    for(var j=0;j<height;j+=50){
      bg.image(tileImage,i,j,50,50);
    }
  }
  image(bg,0,0,width, height); //copy to screen
  
  var col = get(mouseX, mouseY);

  fill(255);
  textSize(50);
  text("fps = " + round(frameRate()), 50, 50);
  text("col = " + col, 50, 100);
  text("frameCount = " + frameCount, 50, 150);
}

Thanks for the report! This may be worth submitting an issue on github. If you could do it that would be great (please link to this thread too).

I replicated the problem here: p5.js Web Editor

What I found is it only happens on Chrome and not on Firefox (my environment is MacBook Air M1). Here’s the screenshot of the profiler on Chrome w/o bg (basically nothing new; getImageData becomes significanly slower after 100 frames)

edit: interestingly, if bg (p5.Graphics) is used, getImageData is not called at all:

edit2: related issue: Add 'willReadFrequently' option to certain getContext('2d') calls · Issue #5840 · processing/p5.js · GitHub

1 Like

By the way, if you only need to read a color from the tiled background, you can optimize this way too

  var x = floor(map(mouseX%50, 0, 50, 0, tileImage.width));
  var y = floor(map(mouseY%50, 0, 50, 0, tileImage.height));
  var col = tileImage.get(x, y);

Thanks, I will get round to posting this on Github when I’ve researched a bit more.

The Tile optimization code is is cool.

The tiling isn’t the issue it just happened to be what I was working on. Slow down also happens when you draw a lot of images to the screen anywhere (ie not in a grid). For a 50 by 50 image the slowdown happens when about 150 images are drawn on my machine. There has always been a slow down in frameRate the more images are drawn to the screen - I’d expect that, and it happened in Processing too, but it would be slower from the start. What is odd is that the slowdown happens after 100 get calls, and can be precipitous - from 40fps to about 12.

My guess is that Chrome is doing some weird optimization (which rather slows down) and that is programmed to be after 100 frames.

My advice is to post what you found so far on github. You don’t have to have a comprehensive report, and it is more important for others to replicate the problem and to investigate it :slight_smile: