1

Is anyone else seeing a major memory leak in the Chromium GPU process on large HTML canvases?

I have code such as the following which uses the standard technique to "sharpen" a canvas on high DPI displays by scaling it:

var dpiScale = ...;  // e.g. 2, 2.25, 3
var width = 1500, height = 800;
canvas.width = width * dpiScale;
canvas.style.width = width + "px";
canvas.height = height * dpiScale;
canvas.style.height = height + "px";
canvas.getContext("2d").scale(dpiScale, dpiScale);

What I am seeing is an epic memory leak in the Chromium GPU process if there are frequent updates to the canvas (where frequent is a few times per second, potentially much more if there is user interaction such as scrolling or pinch-and-zoom which repeatedly and quickly changes the canvas contents). The GPU process allocates memory in large quantities, never frees it, and quite quickly falls over.

The issue appears to have the following characteristics:

  • Applies to Chrome and Edge, but not to Safari or Firefox (hence I'm classing this as "Chromium")
  • Seems to be a recent change, or at least to have worsened significantly in recent versions, or else I believe I'd've spotted it before
  • Seems to be related to the (scaled) canvas size; a threshold beyond which problems start. Removing the scaling (dpiScale = 1) removes the problem. Using scaling but with a smaller canvas also seems to remove the problem.
  • Doesn't appear to relate to the use of any specific canvas primitives. I've reduced the canvas activity to basics such as moveTo(), lineTo(), stroke(), and fill() and can still observe the problem. I'm not drawing images on the canvas. I've specifically (and unsuccessfully) tried removing all uses of fillText(). Despite the mention of {willReadFrequently: true} below, I am not reading from the canvas using getImageData().

What I find very interesting is that there are two workarounds which are effective:

  • Throttle the frequency of canvas updates. Instead of using requestAnimationFrame(), artificially throttle updates so that they occur much less frequently, such as 20fps.
  • Use getContext("2d", {willReadFrequently: true})

The second of these is particularly interesting. The documentation isn't great, but I believe that it's turning off hardware acceleration. To my surprise, there seems to be no real CPU penalty associated with this in the context of the sort of drawing that I'm doing. What happens with this turned on is that the GPU process still starts allocating large amounts of memory during frequent updates (e.g. scrolling), but does then free it when things quieten down again.

I can't absolutely rule out the notion that this is in fact a graphics driver issue rather than a browser issue, or the interaction of Chromium with the graphics driver. I don't seem to be able to replicate the problem on all computers, and it doesn't happen absolutely 100% of the time on my test machine.

While I have the workaround of {willReadFrequently: true}, I'm wondering if anyone has experienced the same thing, and has:

  • Any further insights
  • A better workaround than {willReadFrequently: true}
  • A link to a specific canvas action - something which I shouldn't be doing, or could stop doing - which I've overlooked, and is the specific cause of the problem

?

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
user13473954
  • 111
  • 1
  • 5
  • 1
    Please consider raising an issue in Chromium’s bug tracker at https://bugs.chromium.org/p/chromium/issues/entry. They expect you to provide a reproducible case that a Chromium engineer can use to reproduce the problem. Lacking that, it’s very unlikely any engineers from the project will investigate it. But the _“I don't seem to be able to replicate the problem on all computers, and it doesn't happen absolutely 100% of the time on my test machine”_ statement in the question seems to make it by definition not reproducible. And so there’s very little chance anyone would invest time to investigate – sideshowbarker Dec 15 '22 at 10:21
  • Does it crash the page? You may just be seeing memory waiting to be cleaned up. If it is a leak then the page MUST crash (or loss context GPU) as the leak consumes available RAM. No crash then memory must be free to reuse. – Blindman67 Dec 15 '22 at 13:27
  • @Blindman67 - Yes, the GPU process eventually exhausts all available memory, crashes, and restarts. The nature of the problem is that the GPU process allocates memory and never, ever releases it, even if all canvas activity from the tab stops. The only way of releasing the memory is to close the tab. – user13473954 Dec 15 '22 at 13:31
  • I can not reproduce Win11 Chrome 108.0.5359.125 (Official Build) (64-bit). Scaled canvas by 4 resizing (by a few pixels each frame) and displaying as fast as it can *well above 60fps)" – Blindman67 Dec 15 '22 at 13:38
  • Does GPU process release the memory if you close the offending tab before it crashes? Could be a driver problem. Also check chrome://gpu/ (in addressbar) if there are any known problems – Blindman67 Dec 15 '22 at 13:43
  • @Blindman67 - Yes, closing the tab at any time before a crash does release the memory. I'm also suspicious [as per my description] that it might be a driver problem, or Chrome + specific driver. Thanks for the tip re chrome://gpu/ – user13473954 Dec 15 '22 at 14:44

0 Answers0