Display设备管理

DisplayDevice介绍

DisplayDevice描述了一个显示设备,是Android显示设备的抽象类。Android系统中定义了三种Device

        DISPLAY_PRIMARY     = HWC_DISPLAY_PRIMARY,
        DISPLAY_EXTERNAL    = HWC_DISPLAY_EXTERNAL,
        DISPLAY_VIRTUAL     = HWC_DISPLAY_VIRTUAL,

1:DISPLAY_PRIMARY Android 手机主显示屏
2:DISPLAY_EXTERNAL 一般为外接显示设备
3:DISPLAY_VIRTUAL 虚拟显示设备,用于WiFi投屏

第一种类型是基本的设备,基本所有设备都有自带显示屏, 其他两种则需要其他硬件来支持

SurfaceFlinger中需要显示的图层(layer)将通过DisplayDevice对象传递到OpenGLES中进行合成,因为如果同时有多个显示屏的情况下,不同的Layer可能会显示在不同的屏幕上。合成之后的图像再通过HWComposer对象传递到Framebuffer中显示。DisplayDevice对象中的成员变量mVisibleLayersSortedByZ保存了所有需要显示在本显示设备中显示的Layer对象,同时DisplayDevice对象也保存了和显示设备相关的显示方向、显示区域坐标等信息。
SurfaceFlinger的init函数中会初始化Display设备,代码如下:

  //先检查硬件设备是否连接,或者是否是主显示屏
  if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) {
            // All non-virtual displays are currently considered secure.
            bool isSecure = true;
            //给显示设备分配一个Token,并且创建一个DisplayDeviceState对象,保存在SurfaceFlinger的mCurrentState.display列表中
            createBuiltinDisplayLocked(type);
            wp<IBinder> token = mBuiltinDisplays[i];
            
            //为当前设备创建一个Buffer队列,初始化一个生产者和一个消费者,消费者有FrameBufferSurface封装,生产者交由DisplayDevice持有
            sp<IGraphicBufferProducer> producer;
            sp<IGraphicBufferConsumer> consumer;
            BufferQueue::createBufferQueue(&producer, &consumer,
                    new GraphicBufferAlloc());
            //
            sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i,
                    consumer);
            //为甚分配一个ID
            int32_t hwcId = allocateHwcDisplayId(type);
            //根据相关信息,创建DisplayDevice对象
            sp<DisplayDevice> hw = new DisplayDevice(this,
                    type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
                    fbs, producer,
                    mRenderEngine->getEGLConfig());
            if (i > DisplayDevice::DISPLAY_PRIMARY) {
                // FIXME: currently we don't get blank/unblank requests
                // for displays other than the main display, so we always
                // assume a connected display is unblanked.
                ALOGD("marking display %zu as acquired/unblanked", i);
                hw->setPowerMode(HWC_POWER_MODE_NORMAL);
            }
            //将DiplayDevice保存到SurfaceFlinger的mDisplays中。
            mDisplays.add(token, hw);
        }
    }

     getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);

1:调用了HWComposer的isConnected来检查显示设备是否已连接,只有连接的硬件设备才会创建对应的DisplayDevice。主设备除外,无论是否连接都会创建一个DisplayDevice。
2:为当前显示设备创建一个BBinder类型的Token,并且创建一个DisplayDeviceState对象,保存在SurfaceFlinger的mCurrentState.display列表中
3:为当前显示设备创建一个Buffer队列,初始化一个生产者和一个消费者,消费者有FrameBufferSurface封装,生产者交由DisplayDevice持有
4:创建DisplayDevice对象,传入Buffer队列的producer,DisplayDevice的构造函数中,会创建一个Surface对象传递给底层的OpenGL ES使用,而这个Surface是一个生产者。在OpenGl ES中合成好了图像之后会将图像数据写到Surface对象中,这将触发consumer对象的onFrameAvailable函数被调用,然后FramebufferSurface调用HWComposer对象mHwc的fbPost函数输出到FB中。
5:将DiplayDevice保存到SurfaceFlinger的mDisplays中。
6:调用主显示屏的makeCurrent,绑定DisplayDevice的上下文

DisplayDevice构造方法如下:

    mNativeWindow = new Surface(producer, false);
    ANativeWindow* const window = mNativeWindow.get();

    /*
     * Create our display's surface
     */

    EGLSurface surface;
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (config == EGL_NO_CONFIG) {
        config = RenderEngine::chooseEglConfig(display, format);
    }
    surface = eglCreateWindowSurface(display, config, window, NULL);
    eglQuerySurface(display, surface, EGL_WIDTH,  &mDisplayWidth);
    eglQuerySurface(display, surface, EGL_HEIGHT, &mDisplayHeight);

    if (mType >= DisplayDevice::DISPLAY_VIRTUAL)
        window->setSwapInterval(window, 0);

    mConfig = config;
    mDisplay = display;
    mSurface = surface;
    mFormat  = format;
    mPageFlipCount = 0;
    mViewport.makeInvalid();
    mFrame.makeInvalid();

    setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);

创建OpenGL的EGL环境,我们在前面已经解释过,初始化过后,openGL就可以想Surface中绘制了,绘制完成调用eglswapBuffer来将绘制完成的Buffer发送到Buffer队列,这样FrameBufferSurface就可收到onFrameAvaliable通知了。

接着看下DisplayDevice的swapBuffer函数

void DisplayDevice::swapBuffers(HWComposer& hwc) const {
    // 调用eglSwapBuffers()函数的条件:
    //  (1) 没有HWComposer硬件
    //  (2) 这一帧我们需要使用GLES来合成(包括两种情况)
    //    (a) we have framebuffer target support (not present on legacy
    //        devices, where HWComposer::commit() handles things); or
    //    (b) 虚拟显示设备
    if (hwc.initCheck() != NO_ERROR ||
            (hwc.hasGlesComposition(mHwcDisplayId) &&
             (hwc.supportsFramebufferTarget() || mType >= DISPLAY_VIRTUAL))) {
        //调用eglSwapBuffers更新队列
        EGLBoolean success = eglSwapBuffers(mDisplay, mSurface);
        ......
    }
}

swapBuffers用于将Display合成的图像渲染到FB设备的图像缓冲区,一般只有在不支持HWComposer情况下才会调用。
当DisplayDevice调用swapBuffer函数后,消费者收到onFrameAvailable通知。再来看下FrameBufferSurface的onFrameAvailale函数。

// Overrides ConsumerBase::onFrameAvailable(), does not call base class impl.
void FramebufferSurface::onFrameAvailable(const BufferItem& /* item */) {
    sp<GraphicBuffer> buf;
    sp<Fence> acquireFence;
    //调用NextBuffer从Buffer队列中获取新的Buffer
    status_t err = nextBuffer(buf, acquireFence);
    if (err != NO_ERROR) {
        ALOGE("error latching nnext FramebufferSurface buffer: %s (%d)",
                strerror(-err), err);
        return;
    }
    //调用gralloc模块的FB设备的函数fbPost将graphicBuffer图像缓冲区渲染到FB显示缓冲区中.
    err = mHwc.fbPost(mDisplayType, acquireFence, buf);
    if (err != NO_ERROR) {
        ALOGE("error posting framebuffer: %d", err);
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容