Android Qcom Display学习(十二)

该系列文章总目录链接与各部分简介: 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.cppSkiaGLRenderEngine::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说是高通私有的一个格式,所以可能造成图像无法正常查看问题。
Android Qcom Display学习(十二)_第1张图片
最后总结一下部分送显的流程,说明以下上述dump的添加位置,图片来自于Android 12(S) 图像显示系统 - GraphicBuffer同步机制 - Fence
Android Qcom Display学习(十二)_第2张图片

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切换机制)

你可能感兴趣的:(Android_Display,android,学习)