Drawing images with the image function seems to be very slow

I’m trying to write a game engine using p5, but I’ve hit a huge snag: unless I’m doing something wrong (which is definitely possible), the image function seems really slow. If I call it even just about 40 or 50 times per frame, the frame rate starts to tank, certainly so if I’m calling image hundreds of times per frame as I was expecting to do. Am I doing something silly, or is this the expected behavior?

Here is the code for a sample I put together:

let img;
let heroX = 350;
let heroY = 350;
let speed = 256;
let desiredFrameRate = 60;

function preload()
{
  img = loadImage("TestSprite.png");
}

function setup()
{
  createCanvas(400, 400);
  frameRate(desiredFrameRate);
}

function draw()
{
  background(20);
  
  imageMode(CENTER);
  
  push();
  {
    for (let i = 0; i < 10; ++i)
    {
      for (let j = 0; j < 5; ++j)
      {
        tint(0, 0, 255, 255);
        image(img, 8 + i * 16, 8 + j * 16);
      }
    }
  }
  pop();
  
  let movement = createVector();
  if (keyIsDown(RIGHT_ARROW))
    movement.x += 1;
  if (keyIsDown(LEFT_ARROW))
    movement.x -= 1;
  if (keyIsDown(DOWN_ARROW))
    movement.y += 1;
  if (keyIsDown(UP_ARROW))
    movement.y -= 1;
  movement = movement.normalize().mult(speed / desiredFrameRate);
  heroX += movement.x;
  heroY += movement.y;
  
  push();
  {
    image(img, heroX, heroY);
  }
  pop();
}

what’s the image size? can you post the code and assets on the web editor?

tint is a pixel-wise operation (on the spot, no caching) so it’s very slow. If you want to have fixed values for tint, I highly recommend rendering the images already with a tinted color in a graphics editor, e.g., gimp. Another way is to prepare tinted p5.Graphics (ref), but I recommend the former.

2 Likes

I’m really sorry it took me so long to reply to this. I want to mention that I inadvertently had things set up so that Chrome wasn’t using my GPU, so it was being X-tra slow. Having said that, here is my project:
https://editor.p5js.org/modusponens/sketches/iJ7bp3lVS

Note that it’s still kind of slow if you set useWebGl to true and you have the number of rows and columns turned up kind of high. I want to try to determine how many times I actually can call image per frame and have it not bring down performance.

1 Like

Try to store the background into a separate graphics object.
This way you only have to call the image function once instead of many times (cols x rows).

https://p5js.org/reference/#/p5/createGraphics

3 Likes

Here is a working demo:
https://editor.p5js.org/zuvala/sketches/TT80-whMO

let pg;
let img;

function preload() {
  img = loadImage('https://picsum.photos/20/20'); // Load the image
}

function setup() {
  createCanvas(400, 400);
  pg = createGraphics(400, 400); // <- Create the graphics object
  drawPG(); // <- Function that draws the background
}

function drawPG() {
  for (let i = 0; i < 20; i++) {
    for (let j = 0; j < 20; j++) {
      pg.image(img, i*20, j*20); // <- Note that this draws the image to the pg instead of the canvas.
    }
  }
}

function draw() {  
  image(pg, 0, 0); // <- Display the graphics as an image.
  ellipse(mouseX, mouseY, 40, 40);
}


I do like this idea… So if I want to change any of the background tiles at runtime, I would just overwrite that region of the background image and then still draw the image to the screen the same way as ever, right? This seems like good advice.

I dont know your exact case, but this could work.
Are you working on a game? Maybe you have some more “layers” like for the enemies, the interactive elements, the hero, the menu?

Have you checked this library?

1 Like

Hey, this looks pretty cool. I am trying to build my own engine, so this probably does a bunch of stuff that I want to do myself in my own way, but I will certainly peruse it to see how they do things.