p5.js icon indicating copy to clipboard operation
p5.js copied to clipboard

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

Open motis1 opened this issue 1 year ago • 5 comments

Most appropriate sub-area of p5.js?

  • [ ] Accessibility
  • [ ] Color
  • [X] Core/Environment/Rendering
  • [ ] Data
  • [X] DOM
  • [X] Events
  • [ ] Image
  • [ ] IO
  • [ ] Math
  • [ ] Typography
  • [ ] Utilities
  • [ ] WebGL
  • [ ] Build Process
  • [ ] Unit Testing
  • [ ] Internalization
  • [ ] Friendly Errors
  • [ ] Other (specify if possible)

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);
}

motis1 avatar Sep 29 '23 02:09 motis1

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.

davepagurek avatar Sep 29 '23 12:09 davepagurek

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);
}

motis1 avatar Sep 30 '23 02:09 motis1

Is this issue still open for contribution?

Vishal2002 avatar Jan 03 '24 18:01 Vishal2002

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

rayyan21d avatar Jan 18 '24 12:01 rayyan21d

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:

  • The slowdown is less apparent on 1.9.0 than 1.7.0, but still present
  • I don't notice a slowdown in Firefox, just Chrome
  • I can personally reproduce this within the p5 editor

@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.

davepagurek avatar Jan 21 '24 20:01 davepagurek