p5.js
p5.js copied to clipboard
large webGL canvases draw at unpredictable locations
Most appropriate sub-area of p5.js?
- [ ] Accessibility (Web Accessibility)
- [ ] Build tools and processes
- [ ] Color
- [ ] Core/Environment/Rendering
- [ ] Data
- [ ] DOM
- [ ] Events
- [ ] Friendly error system
- [ ] Image
- [ ] IO (Input/Output)
- [ ] Localization
- [ ] Math
- [ ] Unit Testing
- [ ] Typography
- [ ] Utilities
- [X] WebGL
- [ ] Other (specify if possible)
Details about the bug:
I noticed that when trying to draw very large graphics objects to the canvas, it doesn't seem to be drawing in the correct location. I noticed this happening yesterday when using a couple canvases around 5000px, and today again when using canvases > 6000px.
They seem to be drawing down and to the left for some reason. I'm attaching a sketch showing the issue and a screenshot here as well.
https://editor.p5js.org/aferriss/sketches/aMrQqoKF2
- p5.js version: 1.3.1
- Web browser and version: Chrome
- Operating System: Mac OSX
- Steps to reproduce this:
- Create a large graphics object (>6000px)
- Draw some things into that graphics object
- Try to draw that object at the origin.
I can reproduce in Chrome 89.0.4389.114.
It is not happening in Firefox 87 or Safari 14.0.3.
Its two separate bugs - 1: large canvas will not be placed at 0,0- this bug is in the image function. 2: the shape will not be drawn in the correct position on the graphics buffer. I replicate it with other shapes as well. Expected behavior - the shape position will be 0,0 to shape width/height relative to the buffer canvas.
I can confirm that I can replicate the bug even with canvas with the same size (6000) as the buffer. https://editor.p5js.org/DavidWeiss2/sketches/DrcGxko2O
Update:
When canvas2 is made on main canvas using img function, the function starts breaking after the size of canvas2 is increased beyond 5500 roughly. Also this is only happening in webgl and not p2d canvas.
- On Chrome
canvas2behaves okay withp2dbut notwebgl - On Firefox
canvas2behaves okay with bothp2dandwebgl
let canvas2;
function setup() {
createCanvas(400, 400);
// canvas2 = createGraphics(400, 400, WEBGL); // canvas 2 same size as main, no issues
// canvas2 = createGraphics(5500, 5500, WEBGL); // till 5500(approx) no unwanted behaviour
canvas2 = createGraphics(6500, 6500, WEBGL); // position of canvas 2 changed, unwanted
// canvas2 = createGraphics(6500, 6500); // switch between webgl and p2d on chrome to see the bug
}
function draw() {
background(0, 0, 255);
canvas2.background(255, 0, 0);
// behaviour confirmed in both ways
// image(canvas2, 0, 0, width, height);
image(canvas2, 0, 0, width/2, height/2);
noLoop();
}
After debugging and reaching the LOC where the new canvas2(buffer) is drawn on the main canvas.
this.drawingContext.drawImage(cnv, s * sx, s * sy, s * sWidth, s * sHeight, dx, dy, dWidth, dHeight);
the arguments passed are same in both firefox and chrome.

Thanks for helping debug this @AryanKoundal! If the issue is happening in the drawingContext.drawImage call, the bug is likely not something we can directly control. I tried looking into the Chromium and Firefox source code to see what the discrepancy might be, and it looks like it might be due to the precision of the numbers used in drawImage.
Here's the implementation of drawImage in Firefox. Note that the numbers are doubles:

In the implementation in Chromium, it uses SkScalar as the datatype (the implementation for drawing from a source to a destination rectangle uses SkRect, which just has four SkScalars.)

It seems that this is just a float:
:
I imagine this would become a problem when doing calculations to fit one rectangle into another. Maybe we can check if we still have issues if we only drawImage(image, dx, dy) without the other parameters? If so, maybe we can add a check to see if our more complicated function call can be simplified to just dx and dy, and in that case, use that. For cases where we do need the long version (or if the short version still has issues), this might have to be something we decide we can't fix.