Hardware Bitmap
在Oreo
中引入,做用和它的名字同樣,在Graphics Memory
中分配Bitmap
,這一點區別傳統Java Heap
上分配的狀況。java
回顧下傳統的Bitmap
如何分配方式: Android
開發基本使用BitmapFactory
工廠類建立一塊Bitmap
對象,考慮到reuse
和size
放縮優化增長了一個Options
類,它定義在BitmapFactory
中。canvas
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
...
Bitmap bm = null;
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
try {
if (is instanceof AssetManager.AssetInputStream) {
final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
bm = nativeDecodeAsset(asset, outPadding, opts);
} else {
bm = decodeStreamInternal(is, outPadding, opts);
}
...
return bm;
}
複製代碼
BitmapFactory
經過nativeXXX
call
到jni
流程中,最終都是調用BitmapFactory.cpp#doDecode
接口。api
與內存分配相關部分是解析Options
,拿到width、height、format(888,565)
等,缺省的狀況下使用文件流參數,經過HeapAllocator
在native
層malloc
一塊內存。async
bitmap::createBitmap(env, defaultAllocator.getStorageObjAndReset(),bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
複製代碼
最後native
層回調Java
層,並將native Bitmap
傳給Java Bimtap
,因此Bitmap.java
的構造來自native
層回調,理解這一點很是重要,而且Java
層的Bitmap
是經過mNativePtr
關聯到native
層Bitmap
對象。這是Oreo
之前的方式。ide
Hardware Bitmap
又是個啥?優化
它的內存分配路徑不太相同,一樣是native
層doDecode
接口卻使用hwui/Bitmap.cpp
的allocateHardwareBitmap
方法ui
sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap);
}
複製代碼
它向Renderhread
發起一個MethodInvokeRenderTask
,生成紋理上傳GPU
。this
// RenderThread.cpp
sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::OpenGL:
return OpenGLPipeline::allocateHardwareBitmap(*this, skBitmap);
case RenderPipelineType::SkiaGL:
return skiapipeline::SkiaOpenGLPipeline::allocateHardwareBitmap(*this, skBitmap);
case RenderPipelineType::SkiaVulkan:
return skiapipeline::SkiaVulkanPipeline::allocateHardwareBitmap(*this, skBitmap);
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
break;
}
return nullptr;
}
// SkiaOpenGLPipeline.cpp
sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
SkBitmap& skBitmap) {
...
// glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we provide.
// But asynchronous in sense that driver may upload texture onto hardware buffer when we first
// use it in drawing
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, info.width(), info.height(), format, type,
bitmap.getPixels());
GL_CHECKPOINT(MODERATE);
...
}
複製代碼
返回後,GPU中
多了一塊當前進程上傳的Bitmap
紋理,而進程內native
層Bitmap
會被GC
回收。回想下路徑,最開始BitmapFactory.java
發起decode
需求,要求Hareware
類型,jni
流程進入natice
層調用doDecode
接口,graphics/Bitmap.cpp
經過RenderProxy
向渲染線程發送一個MethodInvokeRenderTask
生成Bitmap
紋理,這一切完成後,JNI
流程原路返回完成一次調用,路徑中全部local
reference
所有釋放。至於你可能要問了,HW Bitmap
的最佳實踐是什麼,我也正在項目中嘗試使用它,歡迎更多的想法在留言區。spa