Zooming in on pattern: huge memory spike, canvas becomes black
Migrated from: https://bugs.launchpad.net/inkscape/+bug/1292459
Steps to reproduce:
- open Inkscape
- create a rectangle
- set fill to a pattern (replicates even with very simple patterns, e.g. polka dots)
- zoom in
What happened?
Inkscape becomes slow and allocates multiple gigabytes of memory in the process, filling pretty much all my remaining RAM. At some point, the canvas becomes black.
Zooming out makes the memory go back to normal.
What should have happened?
Inkscape smoothly zooms into the canvas.
Inkscape Version and Operating System:
- Inkscape Version: 1.1-dev (5570b1c4, 2020-02-09)
- Operating System: Windows 10
- Operating System version: 10.0.18362
Summary of comments:
- apparently introduced by switching to the Cairo renderer
0.48.x r10015: ~ 170 MB real
0.48+devel rev <= 12823: ~ 760 MB real
0.48+devel rev >= 12824: ~ 2.1 GB real
- significantly worsened by d2f932ed
This happens because we are being a little lazy. The current approach to pattern rendering is to calculate the size of the pattern tile based on the size of the pattern on the screen and render it to an image surface. However, if the tile is very large and based on e.g. an upscaled bitmap, we end up allocating a giant image for no reason and filling it with lots of interpolated data.
The fix would be to have a limit on the size of the allocated tile, e.g. 5MB. If this threshold is exceeded, we should switch to a different rendering strategy. Instead of rendering the entire tile to an image surface, we should just repeatedly draw it on the target surface inside a group. This group should then be composited using a mask based on the object's fill / stroke area. Unfortunately, this might cause some problems with seams at the edges of the tile.
Reading Krzysztof Kosinski (tweenk)'s comment above, and browsing sp-patter.cpp around 620 I think I understand better what is happening now (in trunk, rev. 14334). The variable pattern_surface of type DrawingSurface is allocated to be of a HUGE size when zoomed in, which sounds ridiculous when thinking of what is happening; the used part of the tile is smaller and smaller the more you zoom in.
The size of the pattern_surface, which is just a mini rendering of the pattern onto a temporary bitmap, should not ever exceed the size of the viewport of the current window. So, a proposed fix for this would be to limit the resolution of the tile bitmap to the size of the viewport.
The following comments what was gave me this idea:
// Oversample the pattern // TODO: find optimum value // TODO: this is lame. instead of using descrim(), we should extract // the scaling component from the complete matrix and use it // to find the optimum tile size for rendering // c is number of pixels in buffer x and y. // Scale factor of 1.1 is too small... see bug #1251039
https://bugs.launchpad.net/inkscape/+bug/1489168 probably has the same cause. Sample file for testing: imagepattern_goes_black_example.svg