-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support HARDWARE Bitmaps in Android O+ in Glide.
From adb shell dumpsys meminfo com.google.android.apps.photos on a 2016 Pixel on OPM1.170816.001: Before: App Summary Pss(KB) ------ Java Heap: 20360 Native Heap: 66032 Code: 93336 Stack: 1192 Graphics: 85892 Private Other: 8956 System: 19944 TOTAL: 295712 TOTAL SWAP PSS: 82 After: App Summary Pss(KB) ------ Java Heap: 20456 Native Heap: 39756 Code: 93384 Stack: 1220 Graphics: 81460 Private Other: 9024 System: 11662 TOTAL: 256962 TOTAL SWAP PSS: 81 These numbers aren't super solid. Some extra invalidations can dump more Bitmaps into our Bitmap pool which will make it look like the steady state memory usage is increasing even though the maximum amount remains the same. That said, I see between a 33% and 50% improvement in native heap usage after this change. This was tested by flinging back and forth in the All grid in Photos. This change is limited to HARDWARE Bitmap support. We ought to also be able to reduce the bitmap pool size in O+ now that we only care about re-using Bitmaps for very small images or while generating thumbnails for the first time. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=165717498
- Loading branch information
Showing
3 changed files
with
152 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
115 changes: 115 additions & 0 deletions
115
library/src/main/java/com/bumptech/glide/load/resource/bitmap/HardwareConfigState.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package com.bumptech.glide.load.resource.bitmap; | ||
|
||
import android.graphics.Bitmap; | ||
import android.graphics.BitmapFactory; | ||
import android.os.Build; | ||
import android.util.Log; | ||
import java.io.File; | ||
|
||
/** | ||
* State and constants for interacting with {@link android.graphics.Bitmap.Config#HARDWARE} on | ||
* Android O+. | ||
*/ | ||
final class HardwareConfigState { | ||
/** | ||
* The minimum size in pixels a {@link Bitmap} must be in both dimensions to be created with the | ||
* {@link Bitmap.Config#HARDWARE} configuration. | ||
* | ||
* <p>This is a quick check that lets us skip wasting FDs (see {@link #FD_SIZE_LIST}) on small | ||
* {@link Bitmap}s with relatively low memory costs. | ||
* | ||
* @see #FD_SIZE_LIST | ||
*/ | ||
private static final int MIN_HARDWARE_DIMENSION = 128; | ||
|
||
/** | ||
* Allows us to check to make sure we're not exceeding the FD limit for a process with hardware | ||
* {@link Bitmap}s. | ||
* | ||
* <p>{@link Bitmap.Config#HARDWARE} {@link Bitmap}s require two FDs (depending on the driver). | ||
* Processes have an FD limit of 1024 (at least on O). With sufficiently small {@link Bitmap}s | ||
* and/or a sufficiently large {@link com.bumptech.glide.load.engine.cache.MemoryCache}, we can | ||
* end up with enough {@link Bitmap}s in memory that we blow through the FD limit, which causes | ||
* graphics errors, Binder errors, and a variety of crashes. | ||
* | ||
* <p>Calling list.size() should be relatively efficient (hopefully < 1ms on average) because | ||
* /proc is an in-memory FS. | ||
*/ | ||
private static final File FD_SIZE_LIST = new File("/proc/self/fd"); | ||
|
||
/** | ||
* Each FD check takes 1-2ms, so to avoid overhead, only check every N decodes. 50 is more or less | ||
* arbitrary. | ||
*/ | ||
private static final int MINIMUM_DECODES_BETWEEN_FD_CHECKS = 50; | ||
|
||
/** | ||
* 700 with an error of 50 Bitmaps in between at two FDs each lets us use up to 800 FDs for | ||
* hardware Bitmaps. | ||
*/ | ||
private static final int MAXIMUM_FDS_FOR_HARDWARE_CONFIGS = 700; | ||
|
||
/** | ||
* The minimum size that will trigger downsampling in {@link BitmapFactory}. | ||
* | ||
* <p>From {@link android.graphics.BitmapFactory.Options#inSampleSize}. | ||
*/ | ||
private static final int MINIMUM_SAMPLE_SIZE = 2; | ||
|
||
private volatile int decodesSinceLastFdCheck; | ||
private volatile boolean isHardwareConfigAllowed = true; | ||
|
||
private static volatile HardwareConfigState instance; | ||
|
||
static HardwareConfigState getInstance() { | ||
if (instance == null) { | ||
synchronized (HardwareConfigState.class) { | ||
if (instance == null) { | ||
instance = new HardwareConfigState(); | ||
} | ||
} | ||
} | ||
return instance; | ||
} | ||
|
||
private HardwareConfigState() { | ||
// Singleton constructor. | ||
} | ||
|
||
boolean setHardwareConfigIfAllowed( | ||
int targetWidth, int targetHeight, BitmapFactory.Options optionsWithScaling) { | ||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { | ||
return false; | ||
} | ||
|
||
boolean result = !optionsWithScaling.inScaled | ||
&& optionsWithScaling.inSampleSize < MINIMUM_SAMPLE_SIZE | ||
&& targetWidth >= MIN_HARDWARE_DIMENSION | ||
&& targetHeight >= MIN_HARDWARE_DIMENSION | ||
// Make sure to call isFdSizeBelowHardwareLimit last because it has side affects. | ||
&& isFdSizeBelowHardwareLimit(); | ||
|
||
if (result) { | ||
optionsWithScaling.inPreferredConfig = Bitmap.Config.HARDWARE; | ||
optionsWithScaling.inMutable = false; | ||
} | ||
return result; | ||
} | ||
|
||
private synchronized boolean isFdSizeBelowHardwareLimit() { | ||
if (++decodesSinceLastFdCheck >= MINIMUM_DECODES_BETWEEN_FD_CHECKS) { | ||
decodesSinceLastFdCheck = 0; | ||
int currentFds = FD_SIZE_LIST.list().length; | ||
isHardwareConfigAllowed = currentFds < MAXIMUM_FDS_FOR_HARDWARE_CONFIGS; | ||
|
||
if (!isHardwareConfigAllowed && Log.isLoggable(Downsampler.TAG, Log.WARN)) { | ||
Log.w(Downsampler.TAG, | ||
"Excluding HARDWARE bitmap config because we're over the file descriptor limit" | ||
+ ", file descriptors " + currentFds | ||
+ ", limit " + MAXIMUM_FDS_FOR_HARDWARE_CONFIGS); | ||
} | ||
} | ||
|
||
return isHardwareConfigAllowed; | ||
} | ||
} |