android Gui系統之SurfaceFlinger(4)

来源:http://www.cnblogs.com/deman/archive/2016/06/23/5600886.html
-Advertisement-
Play Games

8.Vsync 8.1概論 VSYNC(Vertical Synchronization)是一個相當古老的概念,對於游戲玩家,它有一個更加大名鼎鼎的中文名字—-垂直同步。 “垂直同步(vsync)”指的是顯卡的輸出幀數和屏幕的垂直刷新率相同,這完全是一個CRT顯示器上的概念。其實無論是VSYNC還是 ...


8.Vsync

8.1概論

VSYNC(Vertical Synchronization)是一個相當古老的概念,對於游戲玩家,它有一個更加大名鼎鼎的中文名字—-垂直同步。

“垂直同步(vsync)”指的是顯卡的輸出幀數和屏幕的垂直刷新率相同,這完全是一個CRT顯示器上的概念。其實無論是VSYNC還是垂直同步這個名字,

因為LCD根本就沒有垂直掃描的這種東西,因此這個名字本身已經沒有意義。但是基於歷史的原因,這個名稱在圖形圖像領域被沿襲下來。
在當下,垂直同步的含義我們可以理解為,使得顯卡生成幀的速度和屏幕刷新的速度的保持一致。舉例來說,如果屏幕的刷新率為60Hz,那麼生成幀

的速度就應該被固定在1/60 s。

8.2 VSync信號的產生和分發

VSync信號的產生在android_frameworks_native\services\surfaceflinger\DisplayHardware\HWComposer.cpp裡面定義:

HWComposer::HWComposer(
        const sp<SurfaceFlinger>& flinger,
        EventHandler& handler)
    : mFlinger(flinger),
      mFbDev(0), mHwc(0), mNumDisplays(1),
      mCBContext(new cb_context),
      mEventHandler(handler),
      mDebugForceFakeVSync(false),
      mVDSEnabled(false)
{
    for (size_t i =0 ; i<MAX_HWC_DISPLAYS ; i++) {
        mLists[i] = 0;
    }

    for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) {
        mLastHwVSync[i] = 0;
        mVSyncCounts[i] = 0;
    }

    char value[PROPERTY_VALUE_MAX];
    property_get("debug.sf.no_hw_vsync", value, "0");
    mDebugForceFakeVSync = atoi(value);

    bool needVSyncThread = true;

    // Note: some devices may insist that the FB HAL be opened before HWC.
    int fberr = loadFbHalModule();
    loadHwcModule();

    if (mFbDev && mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
        // close FB HAL if we don't needed it.
        // FIXME: this is temporary until we're not forced to open FB HAL
        // before HWC.
        framebuffer_close(mFbDev);
        mFbDev = NULL;
    }

    // If we have no HWC, or a pre-1.1 HWC, an FB dev is mandatory.
    if ((!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
            && !mFbDev) {
        ALOGE("ERROR: failed to open framebuffer (%s), aborting",
                strerror(-fberr));
        abort();
    }

    // these display IDs are always reserved
    for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
        mAllocatedDisplayIDs.markBit(i);
    }

    if (mHwc) {
        ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
              (hwcApiVersion(mHwc) >> 24) & 0xff,
              (hwcApiVersion(mHwc) >> 16) & 0xff);
        if (mHwc->registerProcs) {
            mCBContext->hwc = this;
            mCBContext->procs.invalidate = &hook_invalidate;
            mCBContext->procs.vsync = &hook_vsync;
            if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
                mCBContext->procs.hotplug = &hook_hotplug;
            else
                mCBContext->procs.hotplug = NULL;
            memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
            mHwc->registerProcs(mHwc, &mCBContext->procs);
        }

        // don't need a vsync thread if we have a hardware composer
        needVSyncThread = false;
        // always turn vsync off when we start
        eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);

        // the number of displays we actually have depends on the
        // hw composer version
        if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
            // 1.3 adds support for virtual displays
            mNumDisplays = MAX_HWC_DISPLAYS;
        } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
            // 1.1 adds support for multiple displays
            mNumDisplays = NUM_BUILTIN_DISPLAYS;
        } else {
            mNumDisplays = 1;
        }
    }

    if (mFbDev) {
        ALOG_ASSERT(!(mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)),
                "should only have fbdev if no hwc or hwc is 1.0");

        DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]);
        disp.connected = true;
        disp.format = mFbDev->format;
        DisplayConfig config = DisplayConfig();
        config.width = mFbDev->width;
        config.height = mFbDev->height;
        config.xdpi = mFbDev->xdpi;
        config.ydpi = mFbDev->ydpi;
#ifdef QCOM_BSP
        config.secure = true; //XXX: Assuming primary is always true
#endif
        config.refresh = nsecs_t(1e9 / mFbDev->fps);
        disp.configs.push_back(config);
        disp.currentConfig = 0;
    } else if (mHwc) {
        // here we're guaranteed to have at least HWC 1.1
        for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
            queryDisplayProperties(i);
        }
    }

    // read system property for VDS solution
    // This property is expected to be setup once during bootup
    if( (property_get("persist.hwc.enable_vds", value, NULL) > 0) &&
        ((!strncmp(value, "1", strlen("1"))) ||
        !strncasecmp(value, "true", strlen("true")))) {
        //HAL virtual display is using VDS based implementation
        mVDSEnabled = true;
    }

    if (needVSyncThread) {
        // we don't have VSYNC support, we need to fake it
        mVSyncThread = new VSyncThread(*this);
    }
#ifdef QCOM_BSP
    // Threshold Area to enable GPU Tiled Rect.
    property_get("debug.hwc.gpuTiledThreshold", value, "1.9");
    mDynThreshold = atof(value);
#endif
}
HWComposer
bool needVSyncThread = true;

是否需要模擬產生VSync信號,預設是開啟的。

  // Note: some devices may insist that the FB HAL be opened before HWC.
    int fberr = loadFbHalModule();
    loadHwcModule();

打開fb & hwc設備的HAL模塊。

        if (mHwc->registerProcs) {
            mCBContext->hwc = this;
            mCBContext->procs.invalidate = &hook_invalidate;
            mCBContext->procs.vsync = &hook_vsync;
            if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
                mCBContext->procs.hotplug = &hook_hotplug;
            else
                mCBContext->procs.hotplug = NULL;
            memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
            mHwc->registerProcs(mHwc, &mCBContext->procs);
        }

        // don't need a vsync thread if we have a hardware composer
        needVSyncThread = false;

硬體VSync信號啟動,不需要軟體模擬。

    if (needVSyncThread) {
        // we don't have VSYNC support, we need to fake it
        mVSyncThread = new VSyncThread(*this);
    }

如果需要,啟動VSyncThread線程來開啟軟體模擬。

8.2.1硬體產生

mHwc->registerProcs(mHwc, &mCBContext->procs);

hwc會產生回調:procs.vsync & procs.invalidate 信號。

void HWComposer::vsync(int disp, int64_t timestamp) {
    if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
        {
            Mutex::Autolock _l(mLock);

            // There have been reports of HWCs that signal several vsync events
            // with the same timestamp when turning the display off and on. This
            // is a bug in the HWC implementation, but filter the extra events
            // out here so they don't cause havoc downstream.
            if (timestamp == mLastHwVSync[disp]) {
                ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")",
                        timestamp);
                return;
            }

            mLastHwVSync[disp] = timestamp;
        }

        char tag[16];
        snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
        ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);

        mEventHandler.onVSyncReceived(disp, timestamp);
    }
}

最終會通知mEventHandler的消息,這個handler從那裡來的呢?

void SurfaceFlinger::init(){

 mHwc = new HWComposer(this,
            *static_cast<HWComposer::EventHandler *>(this));
}

Yes,handler就是SurfaceFlinger,對嘛。SurfaceFlinger就是Surface合成的總管,所以這個信號一定會被它接收。

class SurfaceFlinger : public BnSurfaceComposer,
                       private IBinder::DeathRecipient,
                       private HWComposer::EventHandler
 

8.2.2軟體模擬信號

bool HWComposer::VSyncThread::threadLoop() {
    { // scope for lock
        Mutex::Autolock _l(mLock);
        while (!mEnabled) {
            mCondition.wait(mLock);
        }
    }

    const nsecs_t period = mRefreshPeriod;
    const nsecs_t now = systemTime(CLOCK_MONOTONIC);
    nsecs_t next_vsync = mNextFakeVSync;
    nsecs_t sleep = next_vsync - now;
    if (sleep < 0) {
        // we missed, find where the next vsync should be
        sleep = (period - ((now - next_vsync) % period));
        next_vsync = now + sleep;
    }
    mNextFakeVSync = next_vsync + period;

    struct timespec spec;
    spec.tv_sec  = next_vsync / 1000000000;
    spec.tv_nsec = next_vsync % 1000000000;

    int err;
    do {
        err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
    } while (err<0 && errno == EINTR);

    if (err == 0) {
        mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
    }

    return true;
}

判斷系統Vsync信號開關。然後計算下一次刷新的時間點。

 const nsecs_t period = mRefreshPeriod;

刷新間隔,CLOCK_MONOTONIC是從系統開機後的時間間隔(tick累加)

得到需要等待的時間sleep,和下一次vsync信號的時間點。

然後一個do while的操作,來等待信號時間點的到來。

最後,發出這個信號。

這裡還有個情況,就是一開始的地方,mEnable變數,系統可以設置enable來控制vsync信號的產生。

void HWComposer::VSyncThread::setEnabled(bool enabled) 

 

8.3 SurfaceFlinger處理Vsync信號

在4.4以前,Vsync的處理通過EventThread就可以了。但是KK再次對這段邏輯進行細化和複雜化。Google真是費勁心思為了提升性能。

先來直接看下Surfaceflinger的onVSyncReceived函數:

void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
    bool needsHwVsync = false;

    { // Scope for the lock
        Mutex::Autolock _l(mHWVsyncLock);
        if (type == 0 && mPrimaryHWVsyncEnabled) {
            needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);//mPromaryDisplays是什麼?
        }
    }

    if (needsHwVsync) {
        enableHardwareVsync();//做了什麼
    } else {
        disableHardwareVsync(false);//做了什麼
  } 

}

雖然很短,但是乍一看還是一頭霧水

mPrimaryHWVsyncEnabled是什麼時候被賦值的?

mPrimaryDispSync是什麼,addResyncSample又做了什麼?

enableHardwareVsync &disableHardwareVsync在乾什麼?

要解答這些問題,就從SurfaceFlinger::init開始看

void SurfaceFlinger::init() {
    ALOGI(  "SurfaceFlinger's main thread ready to run. "
            "Initializing graphics H/W...");

    status_t err;
    Mutex::Autolock _l(mStateLock);

    /* Set the mask bit of the sigset to block the SIGPIPE signal */
    sigset_t sigMask;
    sigemptyset (&sigMask);
    sigaddset(&sigMask, SIGPIPE);
    sigprocmask(SIG_BLOCK, &sigMask, NULL);

    // initialize EGL for the default display
    mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(mEGLDisplay, NULL, NULL);

    // Initialize the H/W composer object.  There may or may not be an
    // actual hardware composer underneath.
    mHwc = new HWComposer(this,
            *static_cast<HWComposer::EventHandler *>(this));

    // get a RenderEngine for the given display / config (can't fail)
    mRenderEngine = RenderEngine::create(mEGLDisplay, mHwc->getVisualID());

    // retrieve the EGL context that was selected/created
    mEGLContext = mRenderEngine->getEGLContext();

    LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT,
            "couldn't create EGLContext");

    // initialize our non-virtual displays
    for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
        DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i);
        // set-up the displays that are already connected
        if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) {
#ifdef QCOM_BSP
            // query from hwc if the non-virtual display is secure.
            bool isSecure = mHwc->isSecure(i);;
#else
            // All non-virtual displays are currently considered secure
            bool isSecure = true;
#endif
            createBuiltinDisplayLocked(type, isSecure);
            wp<IBinder> token = mBuiltinDisplays[i];

            sp<IGraphicBufferProducer> producer;
            sp<IGraphicBufferConsumer> consumer;
            BufferQueue::createBufferQueue(&producer, &consumer,
                    new GraphicBufferAlloc());

            sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i,
                    consumer);
            int32_t hwcId = allocateHwcDisplayId(type);
            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);
            }
            mDisplays.add(token, hw);
        }
    }

    // make the GLContext current so that we can create textures when creating Layers
    // (which may happens before we render something)
    getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);

    // start the EventThread
    sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
            vsyncPhaseOffsetNs, true, "app");
    mEventThread = new EventThread(vsyncSrc);
    sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
            sfVsyncPhaseOffsetNs, true, "sf");
    mSFEventThread = new EventThread(sfVsyncSrc);
    mEventQueue.setEventThread(mSFEventThread);

    mEventControlThread = new EventControlThread(this);
    mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
    android_set_rt_ioprio(mEventControlThread->getTid(), 1);

    // set a fake vsync period if there is no HWComposer
    if (mHwc->initCheck() != NO_ERROR) {
        mPrimaryDispSync.setPeriod(16666667);
    }

    // initialize our drawing state
    mDrawingState = mCurrentState;

    // set initial conditions (e.g. unblank default device)
    initializeDisplays();

    // start boot animation
    startBootAnim();
}
SurfaceFlinger::init

有2個幾乎一樣的DispSyncSource,它的目的是提供了Vsync的虛擬化。關於這塊的分析,可以參考Android 4.4(KitKat)中VSync信號的虛擬化

在三緩衝的框架下,對於一幀內容,先等APP UI畫完了,SurfaceFlinger再出場整合到FrameBuffer

而現在google就是讓它們一起跑起來。

然後搞了2個延時,這樣就不會有問題。對應vsyncSrc(繪圖延時) & sfVsyncSrc(合成延時)

8.3.1 EventThread

bool EventThread::threadLoop() {
    DisplayEventReceiver::Event event;
    Vector< sp<EventThread::Connection> > signalConnections;
    signalConnections = waitForEvent(&event);

    // dispatch events to listeners...
    const size_t count = signalConnections.size();
    for (size_t i=0 ; i<count ; i++) {
        const sp<Connection>& conn(signalConnections[i]);
        // now see if we still need to report this event
        status_t err = conn->postEvent(event);
        if (err == -EAGAIN || err == -EWOULDBLOCK) {
            // The destination doesn't accept events anymore, it's probably
            // full. For now, we just drop the events on the floor.
            // FIXME: Note that some events cannot be dropped and would have
            // to be re-sent later.
            // Right-now we don't have the ability to do this.
            ALOGW("EventThread: dropping event (%08x) for connection %p",
                    event.header.type, conn.get());
        } else if (err < 0) {
            // handle any other error on the pipe as fatal. the only
            // reasonable thing to do is to clean-up this connection.
            // The most common error we'll get here is -EPIPE.
            removeDisplayEventConnection(signalConnections[i]);
        }
    }
    return true;
}

 EventThread會發送消息到surfaceflinger裡面的MessageQueue。

MessageQueue處理消息:

int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) {
    ssize_t n;
    DisplayEventReceiver::Event buffer[8];
    while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
        for (int i=0 ; i<n ; i++) {
            if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
#if INVALIDATE_ON_VSYNC
                mHandler->dispatchInvalidate();
#else
                mHandler->dispatchRefresh();
#endif
                break;
            }
        }
    }
    return 1;
}

如果Event的類型是

DisplayEventReceiver::DISPLAY_EVENT_VSYNC

正是我們需要的類型,所以,就有2中處理方式:

UI進程需要先準備好數據(invalidate),然後Vsync信號來了以後,就開始刷新屏幕。

SurfaceFlinger是在Vsync來臨之際準備數據然後刷新,還是平常就準備當VSync來臨是再刷新。

先來看dispatchInvalidate,最後處理的地方就是這裡。

        case MessageQueue::INVALIDATE: {
            bool refreshNeeded = handleMessageTransaction();
            refreshNeeded |= handleMessageInvalidate();
            refreshNeeded |= mRepaintEverything;
            if (refreshNeeded) {
                // Signal a refresh if a transaction modified the window state,
                // a new buffer was latched, or if HWC has requested a full
                // repaint
                signalRefresh();
            }
            break;
        }

我們來看下handleMessageRefresh:

void SurfaceFlinger::handleMessageRefresh() {
    ATRACE_CALL();
    preComposition();  //合成前的準備
    rebuildLayerStacks();//重新建立layer堆棧
    setUpHWComposer();//HWComposer的設定
#ifdef QCOM_BSP
    setUpTiledDr();
#endif
    doDebugFlashRegions();
    doComposition();  //正式合成工作
    postComposition(); //合成的後期工作
}

 

8.3.2handleMessageTransaction

 handleMessageTransaction在簡單判斷後直接調用handlerTransaction。

可以看到裡面的handleTransactionLocked才是代碼真正處理的地方。

void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
{
    const LayerVector& currentLayers(mCurrentState.layersSortedByZ);
    const size_t count = currentLayers.size();

    /*
     * Traversal of the children
     * (perform the transaction for each of them if needed)
     */

    if (transactionFlags & eTraversalNeeded) {
        for (size_t i=0 ; i<count ; i++) {
            const sp<Layer>& layer(currentLayers[i]);
            uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
            if (!trFlags) continue;

            const uint32_t flags = layer->doTransaction(0);
            if (flags & Layer::eVisibleRegion)
                mVisibleRegionsDirty = true;
        }
    }

    /*
     * Perform display own transactions if needed
     */

    if (transactionFlags & eDisplayTransactionNeeded) {
        // here we take advantage of Vector's copy-on-write semantics to
        // improve performance by skipping the transaction entirely when
        // know that the lists are identical
        const KeyedVector<  wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays);
        const KeyedVector<  wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
        if (!curr.isIdenticalTo(draw)) {
            mVisibleRegionsDirty = true;
            const size_t cc = curr.size();
                  size_t dc = draw.size();

            // find the displays that were removed
            // (ie: in drawing state but not in current state)
            // also handle displays that changed
            // (ie: displays that are in both lists)
            for (size_t i=0 ; i<dc ; i++) {
                const ssize_t j = curr.indexOfKey(draw.keyAt(i));
                if (j < 0) {
                    // in drawing state but not in current state
                    if (!draw[i].isMainDisplay()) {
                        // Call makeCurrent() on the primary display so we can
                        // be sure that nothing associated with this display
                        // is current.
                        const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDevice());
                        defaultDisplay->makeCurrent(mEGLDisplay, mEGLContext);
                        sp<DisplayDevice> hw(getDisplayDevice(draw.keyAt(i)));
                        if (hw != NULL)
                            hw->disconnect(getHwComposer());
                        if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
                            mEventThread->onHotplugReceived(draw[i].type, false);
                        mDisplays.removeItem(draw.keyAt(i));
                    } else {
                        ALOGW("trying to remove the main display");
                    }
                } else {
                    // this display is in both lists. see if something changed.
                    const DisplayDeviceState& state(curr[j]);
                    const wp<IBinder>& display(curr.keyAt(j));
                    if (state.surface->asBinder() != draw[i].surface->asBinder()) {
                        // changing the surface is like destroying and
                        // recreating the DisplayDevice, so we just remove it
                        // from the drawing state, so that it get re-added
                        // below.
                        sp<DisplayDevice> hw(getDisplayDevice(display));
                        if (hw != NULL)
                            hw->disconnect(getHwComposer());
                        mDisplays.removeItem(display);
                        mDrawingState.displays.removeItemsAt(i);
                        dc--; i--;
                        // at this point we must loop to the next item
                        continue;
                    }

                    const sp<DisplayDevice> disp(getDisplayDevice(display));
                    if (disp != NULL) {
                        if (state.layerStack != draw[i].layerStack) {
                            disp->setLayerStack(state.layerStack);
                        }
                        if ((state.orientation != draw[i].orientation)
                                || (state.viewport != draw[i].viewport)
                                || (state.frame != draw[i].frame))
                        {
#ifdef QCOM_BSP
                            int orient = state.orientation;
                            // Honor the orientation change after boot
                            // animation completes and make sure boot
                            // animation is shown in panel orientation always.
                            if(mBootFinished){
                                disp->setProjection(state.orientation,
                                        state.viewport, state.frame);
                                orient = state.orientation;
                            }
                            else{
                                char property[PROPERTY_VALUE_MAX];
                                int panelOrientation =
                                        DisplayState::eOrientationDefault;
                                if(property_get("persist.panel.orientation",
                                            property, "0") > 0){
                                    panelOrientation = atoi(property) / 90;
                                }
                                disp->setProjection(panelOrientation,
                                        state.viewport, state.frame);
                                orient = panelOrientation;
                            }
                            // Set the view frame of each display only of its
                            // default orientation.
                            if(orient == DisplayState::eOrientationDefault and
                                    state.frame.isValid()) {
                                qdutils::setViewFrame(disp->getHwcDisplayId(),
                                    state.frame.left, state.frame.top,
                                    state.frame.right, state.frame.bottom);
                            }
#else
                            disp->setProjection(state.orientation,
                                state.viewport, state.frame);
#endif
                        }
                        if (state.width != draw[i].width || state.height != draw[i].height) {
                            disp->setDisplaySize(state.width, state.height);
                        }
                    }
                }
            }

            // find displays that were added
            // (ie: in current state but not in drawing state)
            for (size_t i=0 ; i<cc ; i++) {
                if (draw.indexOfKey(curr.keyAt(i)) < 0) {
                    const DisplayDeviceState& state(curr[i]);

                    sp<DisplaySurface> dispSurface;
                    sp<IGraphicBufferProducer> producer;
                    sp<IGraphicBufferProducer> bqProducer;
                    sp<IGraphicBufferConsumer> bqConsumer;
                    BufferQueue::createBufferQueue(&bqProducer, &bqConsumer,
                            new GraphicBufferAlloc());

                    int32_t hwcDisplayId = -1;
                    if (state.isVirtualDisplay()) {
                        // Virtual displays without a surface are dormant:
                        // they have external state (layer stack, projection,
                        // etc.) but no internal state (i.e. a DisplayDevice).
                        if (state.surface != NULL) {
                            configureVirtualDisplay(hwcDisplayId,
                                    dispSurface, producer, state, bqProducer,
                                    bqConsumer);
                        }
                    } else {
                        ALOGE_IF(state.surface!=NULL,
                                "adding a supported display, but rendering "
                                "surface is provided (%p), ignoring it",
                                state.surface.get());
                        hwcDisplayId = allocateHwcDisplayId(state.type);
                        // for supported (by hwc) displays we provide our
                        // own rendering surface
                        dispSurface = new FramebufferSurface(*mHwc, state.type,
                                bqConsumer);
                        producer = bqProducer;
                    }

                    const wp<IBinder>& display(curr.keyAt(i));
                    if (dispSurface != NULL && producer != NULL) {
                        sp<DisplayDevice> hw = new DisplayDevice(this,
                                state.type, hwcDisplayId,
                                mHwc->getFormat(hwcDisplayId), state.isSecure,
                                display, dispSurface, producer,
                                mRenderEngine->getEGLConfig());
                        hw->setLayerStack(state.layerStack);
                        hw->setProjection(state.orientation,
                                state.viewport, state.frame);
                        hw->setDisplayName(state.displayName);
                        // When a new display device is added update the active
                        // config by querying HWC otherwise the default config
                        // (config 0) will be used.
                        int activeConfig = mHwc->getActiveConfig(hwcDisplayId);
                        if (activeConfig >= 0) {
                            hw->setActiveConfig(activeConfig);
                        }
                        mDisplays.add(display, hw);
                        if (state.isVirtualDisplay()) {
                            if (hwcDisplayId >= 0) {
                                mHwc->setVirtualDisplayProperties(hwcDisplayId,
                                        hw->getWidth(), hw->getHeight(),
                                        hw->getFormat());
                            }
                        } else {
                            mEventThread->onHotplugReceived(state.type, true);
                        }
                    }
                }
   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Gradle Project項目、Module模塊導入 最近看到網上很多人在抱怨,Android Studio很難導入github上下載下來的一些項目,主要包括: 1、導入就在下載Gradle2、根本導不進 下麵我分2部分來講解正確的導入姿勢: 1、導入完整的Github Project 我們在gi ...
  • 1、okHttp https://github.com/square/okhttp 2、okhttp-utils https://github.com/hongyangAndroid/okhttp-utils 3、NoHttp https://github.com/yanzhenjie/NoHttp ...
  • 從電腦上徹底刪除HelloWorld項目 回到as歡迎頁面 複製github中項目的地址 點擊clone ...
  • 首先下載安裝git 下載地址:https://git-scm.com/ 打開AS,並設置如下: 到github上面註冊一個帳號 運行--cmd cd C:\Program Files\Git\bin 接著運行 回到AS 點擊VCS按鈕 點擊share 預設情況下選中全部文件 搞定 ...
  • Apple在 WWDC 2016 上針對iOS提供的全新 消息框架(Messages framework) 使得開發者能夠創建與Apple的“信息”應用進行交互的應用擴展。新增的消息API目前支持兩類擴展: 貼紙包(Sticker Pack),提供了一系列可供用戶插入到消息中發佈的圖片。 iMess ...
  • 一、經典的第三方庫 1、MJRefresh 上拉刷新和下拉添加 2、AFNetworking 網路請求,請求回來的是字典 3、MJExtension 將字典與模型之間相互轉換,還有其他的轉化自己摸索使用吧 4、資料庫:FMDB 把系統自帶的資料庫封裝後更好的去使用 二、簡單使用的第三方庫 1、IQK ...
  • 本文的本本win7 64bit 6G android studio2.1 在運行程式的時候Gradle Build Running 特別慢,一個helloworld都快2min了 1、開啟gradle單獨的守護進程 在下麵的目錄下麵創建gradle.properties文件: /home/<user ...
  • ZIP壓縮包文件中允許存在“../”的字元串,攻擊者可通過精心構造ZIP文件,利用多個“../”從而改變ZIP包中某個文件的存放位置,覆蓋替換掉應用原有的文件。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...