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