该系列文章总目录链接与各部分简介: Android Qcom Display学习(零)
本章主要是基于高通平台上dump出GPU渲染 or GPU合成 or HWC合成的GraphicBuffer的数据。
起初是在B站上看到这么一个视频,能dump出每个Layer的数据显示系统原理以及图形系统调试,看到Android Framework中自带了
Layer::dump的方法,但是在Android S上并没有,在Google的历史中找到移植时发现Android版本的更替Layer.cpp中的改动很大
https://android.googlesource.com/platform/frameworks/native/+/e64a79c/services/surfaceflinger/Layer.cpp
https://android.googlesource.com/platform/frameworks/native/+/e64a79c/services/surfaceflinger/SurfaceFlinger.cpp
于是自己写了个dump的方法,结果发现在Layer.cpp中通过getBuffer()拿到的数据一直是空的,又想通过layerSettings.source.buffer.buffer来获取GraphicBuffer,最终找到了frameworks/native/libs/renderengine/skia/SkiaGLRenderEngine.cpp中SkiaGLRenderEngine::drawLayers
static void dumpBuffer(void* addr, uint32_t w, uint32_t h, uint32_t s, PixelFormat f){
if( !addr){
ALOGE("Addr is NULL");
return;
}
static int count = 0;
char filename[100];
memset(filename, 0, sizeof(filename));
sprintf(filename, "/data/dump/layer_gpu_%d_frame_%d_%d_%d.rgb", count, w, h, f);
ALOGD("dump GraphicBUffer to RGB file:%s",filename);
count ++;
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if(fd < 0) return;
void* base = addr;
//uint32_t c= dataSpaceToInt(d);
//write(fd, &w, 4);
//write(fd, &h, 4);
//write(fd, &f, 4);
//write(fd, &c, 4);
size_t Bpp = bytesPerPixel(f);
for (size_t y = 0; y < h; y++) {
write(fd, base, w*Bpp);
base = (void *)((char *)base + s*Bpp);
}
close(fd);
}
static void dumpLayers(const sp<GraphicBuffer>& target){
void *addr = NULL;
uint32_t w, s, h, f;
w = target->getWidth();
h = target->getHeight();
s = target->getStride();
f = target->getPixelFormat();
int result = target->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &addr);
if(result < 0){
ALOGE("lock buffer failed: %d", result);
}
dumpBuffer(addr, w, h, s, f);
}
拿到dump出的GraphicBuffer在7YUV和YUVIEW上发现只有星点都不是正常画面,在RenderSurface::queueBuffer中dump GPU合成Layers的GraphicBuffer,发现也是星点,当然这个也是预料之中,Layers的数据都不对合成后的肯定也有问题,
看了这个大佬的博客 Android 图像显示系统 - 导出图层数据的方法介绍(dump GraphicBuffer raw data)
static void dumpGraphicRawData2file(const native_handle_t* bufferHandle,
uint32_t width, uint32_t height,
uint32_t stride, int32_t format)
{
ALOGE("%s [%d]", __FUNCTION__, __LINE__);
static int sDumpCount = 0;
if(bufferHandle != nullptr) {
int shareFd = bufferHandle->data[0];
unsigned char *srcAddr = NULL;
uint32_t buffer_size = stride * height * bytesPerPixel(format);
srcAddr = (unsigned char *)mmap(NULL, buffer_size, PROT_READ, MAP_SHARED, shareFd, 0);
char filename[100];
memset(filename, 0, sizeof(filename));
sprintf(filename, "data/dump/layers_gpu_composer_%d_frame_%d_%d_%d.raw", sDumpCount, width, height, format);
int dumpFd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if(dumpFd >= 0 && srcAddr != NULL) {
ALOGE("jerry dumpFd success");
write(dumpFd, srcAddr, buffer_size);
close(dumpFd);
}
munmap((void*)srcAddr, buffer_size);
}
}
网上搜了一大堆,才知道合成方式有GPU和HWC,可通过adb shell dumpsys SurfaceFlinger > SurfaceFlinger.txt
6 Layers
- Output Layer 0xb400007758f7fa30(Wallpaper BBQ wrapper#0)
hwc: layer=0x088 composition=DEVICE (2)
- Output Layer 0xb400007758f97b00(com.android.launcher3/com.android.searchlauncher.SearchLauncher#0)
hwc: layer=0x089 composition=DEVICE (2)
- Output Layer 0xb400007758f8a620(StatusBar#0)
hwc: layer=0x087 composition=CLIENT (1)
- Output Layer 0xb400007758f87500(NavigationBar0#0)
hwc: layer=0x08a composition=CLIENT (1)
- Output Layer 0xb400007758f9a3f0(ScreenDecorOverlay#0)
hwc: layer=0x085 composition=CLIENT (1)
- Output Layer 0xb400007758f72550(ScreenDecorOverlayBottom#0)
hwc: layer=0x084 composition=CLIENT (1)
在BufferStateLayer::setBuffer中添加dump HWC中的GraphicBuffer,发现还是不正常的,后来在高通文档里发现高通本来就有dump Layers的方法,于是就想看看有没有问题
adb root
adb shell setprop vendor.gralloc.disable_ubwc 1
adb shell setprop vendor.gralloc.enable_fb_ubwc 0
adb shell stop vendor.gralloc-2-0
adb shell start vendor.gralloc-2-0
adb shell stop vendor.qti.hardware.display.allocator
adb shell start vendor.qti.hardware.display.allocator
stop;start // Reboot Android frame to make the change effective
adb shell dumpsys SurfaceFlinger
adb shell setenforce 0
GPU
adb root
adb shell "vndservice call display.qservice 21 i32 2 i32 1 i32 3"
adb pull /data/vendor/display/frame_dump_disp_id_00_builtin/ ./
结果结果发现图像是正常的,然后dump我们自己的GraphicBuffer数据,发现也正常了,右边是GPU合成的Layer(StaturBar和NavigationBar0等),左边是非GPU合成的Layer图(Launcher),最终得出跟ubwc有关,ubwc说是高通私有的一个格式,所以可能造成图像无法正常查看问题。
最后总结一下部分送显的流程,说明以下上述dump的添加位置,图片来自于Android 12(S) 图像显示系统 - GraphicBuffer同步机制 - Fence
VSYNC -> onMessageRefresh -> CompositionEngine::present -> Output::present
(1) writeCompositionState -> writeStateToHWC -> writeOutputIndependentPerFrameStateToHWC -> writeBufferStateToHWC->
Layer::setBuffer -> Composer::setLayerBuffer
合成方式Composition::CLIENT会被Ingored, Device的合成会直接送入HWC
(2) setColorTransform
设置颜色矩阵作用于所有layer, Night Light对应护眼模式, Andorid自带模式
(2) prepareFrame -> chooseCompositionStrategy
将合成类型是Client还是Device送入FrameBufferSurface, 默认是Client GPU合成
(3) finishFrame -> composeSurfaces -> SkiaGLRenderEngine::drawLayers -> RenderSurface::queueBuffer ->
advanceFrame -> RenderSurface::nextBuffer -> setClientTarget
drawLayers中会判断hasClientComposition,说明是GPU合成,rendyFence同步判断准备好后queueBuffer放入BufferQueue,
FrameBufferSurface::nextBuffer从BufferQueue中取出后通过setClientTarget送入HWC中
Android 12(S) 图像显示系统 - GraphicBuffer同步机制 - Fence
Android图像首先由CPU将对象处理为多维图像、纹理(Bitmaps,Drawables一起打包到统一的Texture纹理中),然后再传递给GPU,由于CPU和GPU两个硬件是异步的,CPU调用OpenGL等命令时,是由GPU去执行真正的绘图操作,绘图何时结束,CPU时不清楚的,除非等待这些命令完成,因此需要引入Fence同步机制。
Fence的要旨是控制GraphicBuffer的状态,是否允许工作的资源竞争问题。GraphicBuffer相对应的BufferState状态一定程度上说明了该GraphicBuffer的归属,但只指示了CPU里的状态,而GraphicBuffer的真正使用者一般是GPU。比如当生产者把一个GraphicBuffer放入BufferQueue时,只是在CPU层面完成了归属的转移。但GPU说不定还在用,如果还在用的话消费者是不能拿去合成的。这时候GraphicBuffer和生产消费者的关系就比较暧昧了,消费者对GraphicBuffer具有拥有权,但无使用权,它需要等一个信号,告诉它GPU用完了,消费者才真正拥有使用权。
VSync的要旨是协调硬件和软件之间的工作时序,屏幕的刷新帧率是固定的,因此每规定时间都需要准备好下一帧,如果准备好则会造成画面延迟,一帧显示多次,如果准备过快,则会造成画面撕裂,显示多帧,Vsync信号就是用来控制前后缓冲区切换的节奏(也即是DRM中的PageFlip切换机制)