Open Bug 1163426 Opened 9 years ago Updated 5 months ago

WebGL canvas passed to drawImage is very slow

Categories

(Core :: Graphics: CanvasWebGL, defect, P3)

40 Branch
defect

Tracking

()

People

(Reporter: ashley, Unassigned)

References

()

Details

(Keywords: perf, Whiteboard: gfx-noted webgl-perf [DevAdvocacy])

User Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36

Steps to reproduce:

1. Visit: http://www.scirra.com/labs/bugs/webgl-canvas2d-perf.html
2. Observe the FPS reading


Actual results:

The demo simply creates a window-sized WebGL canvas which changes every frame, and copies it to a window-sized 2D canvas with drawImage(). On a high-end Windows 8.1 laptop, this runs at about ~18 FPS, and on Firefox for Android on a Moto X (2nd gen, Android 5.0) it only gets about 3 FPS.


Expected results:

It should run at close to 60 FPS on all systems, since there is not much work involved (just a window-sized texture copy).

Drawing a WebGL canvas to a 2D canvas is a simple workaround for rendering multiple views of one WebGL context. This is simpler to implement than having to manage shared resources (which aren't actually supported yet), and avoids wasting memory from creating duplicate contexts and loading resources like textures twice. However the performance impact makes it impractical to use this workaround in practice. The performance is so poor it suggests a CPU readback is happening per frame, when it should be possible to fully GPU accelerate this.
Component: Untriaged → Canvas: WebGL
Keywords: perf
Product: Firefox → Core
It's true!
Flags: needinfo?(jgilbert)
Whiteboard: gfx-noted webgl-perf
This is still slow in Nightly 57. It's the main reason scrolling is slow in our PWA Construct 3 (editor.construct.net).
FYI, the impact of this bug is it is now solely responsible for preventing us from launching support for our PWA on Firefox.
We have decided to launch our PWA anyway and will point users at this bug on startup to explain why performance may be poor, with a suggestion to switch to Chrome if they have trouble.
Hey Ashley,
Thanks for bringing this to our attention again, we'll try to get someone to look into this.

FWIW I just checked on a high-end Windows laptop (i7-7500u, 16GB) and latest Firefox Nightly (build 2017-09-15) this still shows 36 FPS as opposed to Chrome's no-sweat 60 FPS.
Whiteboard: gfx-noted webgl-perf → gfx-noted webgl-perf [DevAdvocacy]
It turns out Safari has the same problem: https://bugs.webkit.org/show_bug.cgi?id=177132
As I mentioned before we're basically trying to emulate multiple views of the same WebGL context. Due to Safari also being affected we ended up developing a workaround, where if only one view is visible, we display the source WebGL canvas directly. It's pretty complicated, but it seems to fix the performance problem in Firefox and Safari in the case the user only has one view. If there are multiple views we fall back to the drawImage(webglCanvas) path which is still slow, but I think this is less common. So this is less urgent than it was previously. It would still be useful for this case to be fast, but we can probably get by for the time being - or wait for OffscreenCanvas which looks like it provides a similar capability with a better zero-copy guarantee.
Flags: needinfo?(jgilbert)

Problem is still occurring in Firefox v 89.0.2 64 Bit on Windows 10.

We are using canvas.DrawImage(webGlCanvas) in a 2D/3D charting library. The performance of this is incredibly slow on Firefox relative to Chrome, Safari or Edge.

const sourceCanvas: HTMLCanvasElement; // WebGL canvas
const destCanvas: HTMLCanvasElement; // HTML5 canvas
destCanvas.getContext("2d").drawImage(sourceCanvas, 0, 0, sourceCanvas.width, sourceCanvas.height); // Copy step is extremely slow in firefox

I've also tried a workaround of using GL.readPixels / putImageData but this does not improve performance:

const sourceCanvas: HTMLCanvasElement; // WebGL canvas
const destCanvas: HTMLCanvasElement; // HTML5 canvas
const gl = sourceCanvas.getContext("webgl2");
const pixels = new Uint8ClampedArray(destCanvas.width * destCanvas.height * 4);
gl.readPixels(0,0, destCanvas.width, destCanvas.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
destCanvas.putImageData(new ImageData(pixels, destCanvas.width, destCanvas.height), 0, 0);

also tried workaround of setting imageSmoothingEnabled = false but doesn't make a difference.

destCanvas.imageSmoothingEnabled = false;
Severity: normal → S3

This still reproduces in Firefox 119. The original link is now dead, here's an updated repro: https://downloads.scirra.com/labs/bugs/canvas-draw-webgl-slow.html

With a maximized window, this can only achieve about 22 FPS on a system with a NVIDIA GeForce RTX 4070. It really is unusably slow. Meanwhile Chrome can achieve a smooth 60 FPS.

I tried working around this with transferFromImageBitmap, but that is unusably slow as well - see issue 1864882.

FWIW Safari 17.1 appears to also run this perfectly smoothly, so it seems Firefox is the only browser with a performance problem here.

See Also: → 1864882
You need to log in before you can comment on or make changes to this bug.