pangula / osmdroid

Automatically exported from code.google.com/p/osmdroid
0 stars 0 forks source link

Establish a Bitmap pool for tiles #452

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
When scrolling new tiles get loaded from the tile providers and put into the 
cache. The old tiles get pushed out and eventually recycled by the GC.

This causes a lot of garbage collection and memory thrashing. Consequently 
scrolling can randomly become slower or stuttery.

We should establish a bitmap pool where bitmaps can be reused without being 
GCed and re-allocated. The BitmapFactory.options inBitmap allows a Bitmap to be 
reused. When the LRUCache pushes a tile out it should return it to the pool. 
When the BitmapTileSource needs a tile it should first try to request one from 
the pool and reuse it if available.

Note that threading becomes an issue here. It is possible that tiles could be 
retained from the cache, then get pushed out of the cache and then reused by 
the retaining owner. This may be a problem for things like the ScaleTileLooper 
(where we had issues in the past with recycle() getting called on tiles and 
then the tiles being used in ScaleTileLooper).

Original issue reported on code.google.com by kurtzm...@gmail.com on 19 Jul 2013 at 9:40

GoogleCodeExporter commented 9 years ago
First shot at an implementation. This patch adds a ReusableBitmapDrawable which 
keeps track of references and will only recycle the bitmap if there are no 
remaining references. Documentation is added to 
MapTileProviderBase.getMapTile() to inform the user of their responsibilities 
when the method returns a ReusableBitmapDrawable. Established a BitmapPool to 
keep track of bitmaps ready to be reused. Changed BitmapTileSource to reuse 
Bitmaps from the BitmapPool. Changed LRUMapTileCache to return recyclable 
Bitmaps to the BitmapPool.

We should probably establish a limit to the number of Bitmaps in BitmapPool or 
maybe attach to a low memory event and remove Bitmaps when memory is getting 
squeezed.

Otherwise this makes scrolling much smoother and loses a lot of the jitteriness!

Original comment by kurtzm...@gmail.com on 30 Jul 2013 at 3:23

Attachments:

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
Note that BitmapPool.returnDrawableToPool() should be:

    public void returnDrawableToPool(ReusableBitmapDrawable drawable) {
        Bitmap b = drawable.tryRecycle();
        if (b != null && b.isMutable())
            synchronized (mPool) {
                mPool.addLast(b);
            }
    }

adding an isMutable() check.

Original comment by kurtzm...@gmail.com on 1 Aug 2013 at 6:23

GoogleCodeExporter commented 9 years ago
And we should return a mutable Bitmap always:

    public void applyReusableOptions(BitmapFactory.Options bitmapOptions) {
        Bitmap pooledBitmap = obtainBitmapFromPool();
        bitmapOptions.inBitmap = pooledBitmap;
        bitmapOptions.inSampleSize = 1;
        bitmapOptions.inMutable = true;
    }

Original comment by kurtzm...@gmail.com on 1 Aug 2013 at 6:27

GoogleCodeExporter commented 9 years ago

Original comment by kurtzm...@gmail.com on 17 Aug 2013 at 10:05

GoogleCodeExporter commented 9 years ago
Committed in r1328.

Original comment by kurtzm...@gmail.com on 27 Sep 2013 at 2:54

GoogleCodeExporter commented 9 years ago
Hi,

At BitmapPool at method applyReusableOptions the SDK check needs to be ">= 
Build.VERSION_CODES.HONEYCOMB", as the inBitmap and inMutable are added in API 
level 11.
The Build.VERSION_CODES.GINGERBREAD is API 9, now the osmdroid does not work at 
API 10.

Regards.

Original comment by devemu...@gmail.com on 28 Sep 2013 at 8:51

GoogleCodeExporter commented 9 years ago
I updated the SDK check.

Original comment by neilboyd on 30 Sep 2013 at 7:52

GoogleCodeExporter commented 9 years ago
Wouldn't it be better if the SDK check is >= instead of > as it works with 
HONEYCOMB?

Regards.

Original comment by devemu...@gmail.com on 1 Oct 2013 at 6:20

GoogleCodeExporter commented 9 years ago
Changed. I missed that subtlety in your comment ;)

Original comment by neilboyd on 1 Oct 2013 at 1:58

GoogleCodeExporter commented 9 years ago
This issue was updated by revision r1341.

Revert the changes in r1120 and issue 368. Two reasons - first, the canvas is 
ARGB_8888 and having bitmaps that are RGB_565 causes additional work to blit 
the bitmaps to the screen (see 
http://www.curious-creature.org/2010/12/04/gingerbread-and-32-bits-windows/ and 
follow up article). Second - when we reuse Bitmaps in the BitmapPool they need 
to be ARGB_8888 (the same as the tiles) otherwise tiles that are supposed to be 
transparent (like bike path overlays for instance) can sometimes not be 
transparent anymore.

Original comment by kurtzm...@gmail.com on 1 Oct 2013 at 7:20

GoogleCodeExporter commented 9 years ago
Update issue 452.
Add obtainSizedBitmapFromPool() to get a Bitmap that is a specific size for
reuse when needed. Since we only put map tile-sized bitmaps in there, they
should always be the same size but it doesn't hurt to throw that in there. When
we use Bitmap.Options.inBitmap it is okay if the Bitmap is mis-sized, because it
will just ignore that Bitmap and allocate a properly sized one.
Changed ZoomInTileLooper/ZoomOutTileLooper to use obtainSizedBitmapFromPool()
rather than allocate a new Bitmap. This prevents the BitmapPool from growing
endlessly.

Original comment by kurtzm...@gmail.com on 25 Oct 2013 at 5:24

GoogleCodeExporter commented 9 years ago
We should do one of the following:

 1. Put a hard-limit on the number of Bitmaps the pool can keep. This could be linked to the size of the tile cache.
 2. Purge Bitmaps when low-memory notification is received. We can subscribe to ComponentCallbacks.onLowMemory() in API < 14, and ComponentCallbacks2.onTrimMemory() on newer APIs.

Original comment by kurtzm...@gmail.com on 25 Oct 2013 at 5:29

GoogleCodeExporter commented 9 years ago
    public void returnDrawableToPool(ReusableBitmapDrawable drawable) {
        Bitmap b = drawable.tryRecycle();
        if (b != null && b.isMutable())
            synchronized (mPool) {
                mPool.addLast(b);
            }
    }

Guess will be better to recycle bitmap if it is not mutable to reclaim memory 
as soon as possible and avoid a lot of garbage collection:

    public void returnDrawableToPool(ReusableBitmapDrawable drawable) {
        Bitmap b = drawable.tryRecycle();
        if (b != null) {
            if(b.isMutable()) {
                synchronized (mPool) {
                    mPool.addLast(b);
                }
            } else 
                b.recycle();
        }
    }

Original comment by sergey.alexeev@gmail.com on 4 Jun 2014 at 12:54