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.69k stars 3.33k forks source link

Changing the FPS cap and manipulating pixels causes an permanent FPS drop whenever the cursor clicks/hovers on the page #6444

Open motis1 opened 1 year ago

motis1 commented 1 year ago

Most appropriate sub-area of p5.js?

p5.js version

1.7.0

Web browser and version

Google Chrome: 117.0.5938.92 (Official Build) (64-bit) (cohort: Stable)

Operating System

Windows 10 Pro: 10.0.19045 Build 19045

Steps to reproduce this

Details

Changing the FPS cap and manipulating pixels causes a permanent drop in performance whenever the cursor clicks/hovers on the page.

It also appears that the FPS drop incurred by hovering over the page can only happen for a short period of time after the page has just loaded but clicking seems to work whenever during the lifetime of the page.

P.S. I accidentally created an empty issue whilst trying to create this one. If it could be deleted by someone who can it would be greatly appreciated.

Steps:

  1. Run the code snippet attached to this issue
  2. Hover/Click on the page/canvas and notice the drop in FPS.
  3. If you do not observe a drop in FPS, consider adjusting the SIZE constant so that there is enough load to make the baseline FPS below that of your monitors refresh rate.

Snippet:

const SIZE = 600;
const FRAME_RATE = 999;

function setup() {
    createCanvas(SIZE, SIZE);
    frameRate(FRAME_RATE);
}

function draw() {
    background(0);

    for (let y = 0; y < SIZE; y++) {
        for (let x = 0; x < SIZE; x++) {
            set(x, y, 0);
        }
    }

    updatePixels();

    fill(255);
    text(Math.round(frameRate()), 50, 50);
}
davepagurek commented 1 year ago

Do you see a similar slowdown when using loadPixels(), updating the pixels[] array, then calling setPixels()? It's known that calling set() to update a grid of pixels will be slow, as it tries to update the canvas after each call rather than doing it all at once.

motis1 commented 1 year ago

Yes, it is possible to observe this problem using the pixels[] array along with loadPixels() and updatePixels() instead of the set() method.

Using the following code, it is possible to observe this problem using a simple graph using both the pixels[] and set(). For reasons unknown to me, this bug is not reproducible inside the p5.js editor. For the best results run the JavaScript inside of a basic HTML page with p5.js included using a CDN.

const SIZE = 800;
const FRAME_RATE = 180;

let graphTicks = [];

function drawGraph() {
    noFill();
    stroke(255);
    beginShape();

    for (let i = 0; i < graphTicks.length; i++) {
        let x = (graphTicks[i].timestamp / frameCount) * SIZE;
        let y = SIZE - ((graphTicks[i].frameRate / FRAME_RATE) * SIZE);

        curveVertex(x, y);
    }

    endShape();
}

function useSetFunc() {
    for (let y = 0; y < SIZE; y++) {
        for (let x = 0; x < SIZE; x++) {
            set(x, y, 0);
        }
    }

    updatePixels();
}

function usePixelsArray() {
    loadPixels();

    for (let y = 0; y < SIZE; y++) {
        for (let x = 0; x < SIZE; x++) {
            let d = pixelDensity();
            for (let i = 0; i < d; i++) {
                for (let j = 0; j < d; j++) {
                    // loop over
                    index = 4 * ((y * d + j) * width * d + (x * d + i));
                    pixels[index] = 0;
                    pixels[index + 1] = 0;
                    pixels[index + 2] = 0;
                    pixels[index + 3] = 255;
                }
            }
        }
    }

    updatePixels();
}

function setup() {
    createCanvas(SIZE, SIZE);
    frameRate(FRAME_RATE);
}

function draw() {
    graphTicks.push({
        timestamp: frameCount,
        frameRate: frameRate()
    });

    background(0);

    // Uncomment one of the two functions
    // useSetFunc();
    usePixelsArray();

    drawGraph();

    fill(255);
    noStroke();
    text(Math.round(frameRate()), 50, 50);
}
Vishal2002 commented 10 months ago

Is this issue still open for contribution?

rayyan21d commented 10 months ago

I'm interested in contributing to this issue @motis1 Are there any helpful docs that would help me better understand this?

davepagurek commented 10 months ago

Thanks @Vishal2002 for doing some profiling! It seems like there's not a new function being called after a click, but rather, the set() function just gets slower. Some additional notes from some more testing on my end:

@rayyan21d the way we've been testing so far is with the browser profiler, where each draw call takes longer after clicking compared to before: image

I wonder if this is a known thing in Chrome? Maybe some of you can help look up any existing Chrome/Chromium bug reports that sound similar?

Another way to investigate further: is it also faster on 1.9.0 compared to 1.7.0 for you? If so, you can try using git bisect to find a commit between then and now where the slowness gets better, just so we can get a sense of what affects it. It doesn't look like any significant changes happened between those versions to the set() or updatePixels() methods, just some indentation fixes.