編輯:關於android開發
在上一篇中我們講到,視圖的刷新需要很多步驟,
void SurfaceFlinger::handleMessageRefresh() { ATRACE_CALL(); preComposition(); //合成前的准備 rebuildLayerStacks();//重新建立layer堆棧 setUpHWComposer();//HWComposer的設定 #ifdef QCOM_BSP setUpTiledDr(); #endif doDebugFlashRegions(); doComposition(); //正式合成工作 postComposition(); //合成的後期工作 }
本文將繼續分析這些過程。
invalidate 字面意思就是使無效,更進一步就是當前的buffer已經無限,請刷新界面。
啥也沒干,buffer已經無效,我換下一個,就是handlePageFlip
void SurfaceFlinger::handleMessageInvalidate() { ATRACE_CALL(); handlePageFlip(); }
再來看這個函數:handlePageFlip
bool SurfaceFlinger::handlePageFlip() { Region dirtyRegion; bool visibleRegions = false; const LayerVector& layers(mDrawingState.layersSortedByZ); bool frameQueued = false; // Store the set of layers that need updates. This set must not change as // buffers are being latched, as this could result in a deadlock. // Example: Two producers share the same command stream and: // 1.) Layer 0 is latched // 2.) Layer 0 gets a new frame // 2.) Layer 1 gets a new frame // 3.) Layer 1 is latched. // Display is now waiting on Layer 1's frame, which is behind layer 0's // second frame. But layer 0's second frame could be waiting on display. Vector<Layer*> layersWithQueuedFrames; for (size_t i = 0, count = layers.size(); i<count ; i++) { const sp<Layer>& layer(layers[i]); if (layer->hasQueuedFrame()) { frameQueued = true; if (layer->shouldPresentNow(mPrimaryDispSync)) { layersWithQueuedFrames.push_back(layer.get()); } } } for (size_t i = 0, count = layersWithQueuedFrames.size() ; i<count ; i++) { Layer* layer = layersWithQueuedFrames[i]; const Region dirty(layer->latchBuffer(visibleRegions)); const Layer::State& s(layer->getDrawingState()); invalidateLayerStack(s.layerStack, dirty); } mVisibleRegionsDirty |= visibleRegions; // If we will need to wake up at some time in the future to deal with a // queued frame that shouldn't be displayed during this vsync period, wake // up during the next vsync period to check again. if (frameQueued && layersWithQueuedFrames.empty()) { signalLayerUpdate(); } // Only continue with the refresh if there is actually new work to do return !layersWithQueuedFrames.empty(); } handlePageFlip
@step1:layer->latchBuffer(visibleRegions) 通過該函數鎖定各layer的緩沖區。可以理解這個函數一定與BufferQueue有關。
status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r, mFlinger->mPrimaryDispSync);
上面是latchBuffer的核心語句。SurfaceFlingerConsumer前文已經提過,是client端操作bufferqueue的一個端口。所以這個函數一定是操作bufferqueue的。
// Acquire the next buffer. // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. err = acquireBufferLocked(&item, computeExpectedPresent(dispSync)); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { err = NO_ERROR; } else if (err == BufferQueue::PRESENT_LATER) { // return the error, without logging } else { ALOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); } return err; } // We call the rejecter here, in case the caller has a reason to // not accept this buffer. This is used by SurfaceFlinger to // reject buffers which have the wrong size int buf = item.mBuf; if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, EGL_NO_SYNC_KHR); return NO_ERROR; } // Release the previous buffer. err = updateAndReleaseLocked(item); if (err != NO_ERROR) { return err; }
看了注釋,基本解釋了大體的過程。
1) 請求新的buffer
2)通過rejecter來判斷申請的buffer是否滿足surfaceflinger的要求。
3)釋放之前的buffer
具體流程可以參考如下:
@step2:SurfaceFlinger:invalidateLayerStack來更新各個區域。
首先來看2個Vsync Rate相關的代碼:
virtual void setVsyncRate(uint32_t count) virtual void requestNextVsync()
當count為1時,表示每個信號都要報告,當count =2 時,表示信號 一個間隔一個報告,當count =0時,表示不自動報告,除非主動觸發requestNextVsync
void SurfaceFlinger::preComposition() { bool needExtraInvalidate = false; const LayerVector& layers(mDrawingState.layersSortedByZ); const size_t count = layers.size(); for (size_t i=0 ; i<count ; i++) { if (layers[i]->onPreComposition()) { needExtraInvalidate = true; } } if (needExtraInvalidate) { signalLayerUpdate(); } }
代碼很簡單,其實一共就3步,
1)獲取全部的layer
2)每個layer onPrecomposition
3) layer update
bool Layer::onPreComposition() { mRefreshPending = false; return mQueuedFrames > 0 || mSidebandStreamChanged; }
也就是說,當layer裡存放被queue的frame以後,就會出發layer update.
void SurfaceFlinger::signalLayerUpdate() { mEventQueue.invalidate(); }
最終會調用:
void EventThread::Connection::requestNextVsync() { mEventThread->requestNextVsync(this); }
void EventThread::requestNextVsync( const sp<EventThread::Connection>& connection) { Mutex::Autolock _l(mLock); if (connection->count < 0) { connection->count = 0; mCondition.broadcast();//通知對vsync感興趣的類 } }
那麼誰在等待這個broadcast呢?
還是EventThread
void EventThread::onVSyncEvent(nsecs_t timestamp) { Mutex::Autolock _l(mLock); mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; mVSyncEvent[0].header.id = 0; mVSyncEvent[0].header.timestamp = timestamp; mVSyncEvent[0].vsync.count++; mCondition.broadcast(); }
前文提到mVisibleRegionsDirty這個變量是標記要刷新的可見區域的,我們按字面意思解釋下:髒的可見區域,顧名思義,這就是要刷新的區域,因為buffer已經“髒”了。
@step1:系統的display可能不止一個,存在與mDisplays中。
@step2:computeVisibleRegions這個函數根據所有的layer狀態,得到2個重要的變量。opaqueRegion & dirtyRegion
dirtyRegion是需要被刷新的。 opaqueRegion 不透明區域,應為layer是按Z-order排序的,左右排在前面的opaqueRegion 會擋住後面的layer。
@step3:Region drawRegion(tr.transform( layer->visibleNonTransparentRegion));程序需要進一步得出每個layer 繪制的區域。
系統的display(顯示器)可能不止一個,但是所有的layer都記錄在layersSortedByZ裡面。記錄每個layer屬於那個display的變量是 hw->getLayerStack()
@step4:將結果保存到hw中。
這裡的不透明區域 是很有意義的一個概念,就是我們在Z-order 上,越靠近用戶的時候,值越大,所以是遞減操作。
用於合成surface的功能模塊可以有2個,OpenGL ES & HWC,它的管理實在HWC裡面實現的。
setUpHWComposer 總的來說就干了3件事情。
1)構造Worklist,並且給DisplayData:list 申請空間
2)填充各layer數據
3)報告HWC(有其他地方決定使用那個)
關鍵地方來了,上面的setUpHWComposer 只是交給HWC來負責顯示,真正顯示的地方就在這裡。
1)如何合成
2)如何顯示到屏幕上
合成的流程大體如上圖。
有了概念後,分析源碼:
void SurfaceFlinger::doComposition() { ATRACE_CALL(); const bool repaintEverything = android_atomic_and(0, &mRepaintEverything); for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { const sp<DisplayDevice>& hw(mDisplays[dpy]); if (hw->isDisplayOn()) { // transform the dirty region into this screen's coordinate space const Region dirtyRegion(hw->getDirtyRegion(repaintEverything)); // repaint the framebuffer (if needed) doDisplayComposition(hw, dirtyRegion); ++mActiveFrameSequence; hw->dirtyRegion.clear(); hw->flip(hw->swapRegion); hw->swapRegion.clear(); } // inform the h/w that we're done compositing hw->compositionComplete(); } postFramebuffer(); }
變量mRepaintEverything用於說明,是否需要全部重繪所有內容。如果為true的化,那麼dirtyRegion就是displaydevice的 width & height構成的RECT。
否則由dirtyRegion轉換而來。
doDisplayComposition是每個Display來處理,有可能會用到OpenGL 的接口來交換buffer。
hw->compositionComplete(); 通知HWC合成結束了。
postFramebuffer HWC的Set接口調用。
傳入的參數inDirtyRegion,這就是要刷新的“髒”區域,but,我們的刷新機制,決定了必須是矩形的區域。
So,需要一個最小的矩形,能夠包裹inDirtyRegion的區域。
SWAP_RECTANGLE:系統支持軟件層面的部分刷新,就需要計算這個最小矩形。
PARTIAL_UPDATES:硬件層面的部分刷新,同理需要這個最小矩形。
最後就是重繪這個區域。
bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty) { RenderEngine& engine(getRenderEngine()); const int32_t id = hw->getHwcDisplayId(); HWComposer& hwc(getHwComposer()); HWComposer::LayerListIterator cur = hwc.begin(id); const HWComposer::LayerListIterator end = hwc.end(id); Region clearRegion; bool hasGlesComposition = hwc.hasGlesComposition(id); const bool hasHwcComposition = hwc.hasHwcComposition(id); if (hasGlesComposition) { if (!hw->makeCurrent(mEGLDisplay, mEGLContext)) { ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s", hw->getDisplayName().string()); eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if(!getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext)) { ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting."); } return false; } // Never touch the framebuffer if we don't have any framebuffer layers if (hasHwcComposition) { // when using overlays, we assume a fully transparent framebuffer // NOTE: we could reduce how much we need to clear, for instance // remove where there are opaque FB layers. however, on some // GPUs doing a "clean slate" clear might be more efficient. // We'll revisit later if needed. if(!(mGpuTileRenderEnable && (mDisplays.size()==1))) engine.clearWithColor(0, 0, 0, 0); } else { // we start with the whole screen area const Region bounds(hw->getBounds()); // we remove the scissor part // we're left with the letterbox region // (common case is that letterbox ends-up being empty) const Region letterbox(bounds.subtract(hw->getScissor())); // compute the area to clear Region region(hw->undefinedRegion.merge(letterbox)); // but limit it to the dirty region region.andSelf(dirty); // screen is already cleared here #ifdef QCOM_BSP clearRegion.clear(); if(mGpuTileRenderEnable && (mDisplays.size()==1)) { clearRegion = region; if (cur == end) { drawWormhole(hw, region); } else if(mCanUseGpuTileRender) { /* If GPUTileRect DR optimization on clear only the UnionDR * (computed by computeTiledDr) which is the actual region * that will be drawn on FB in this cycle.. */ clearRegion = clearRegion.andSelf(Region(mUnionDirtyRect)); } } else #endif { if (!region.isEmpty()) { if (cur != end) { if (cur->getCompositionType() != HWC_BLIT) // can happen with SurfaceView drawWormhole(hw, region); } else drawWormhole(hw, region); } } } if (hw->getDisplayType() != DisplayDevice::DISPLAY_PRIMARY) { // just to be on the safe side, we don't set the // scissor on the main display. It should never be needed // anyways (though in theory it could since the API allows it). const Rect& bounds(hw->getBounds()); const Rect& scissor(hw->getScissor()); if (scissor != bounds) { // scissor doesn't match the screen's dimensions, so we // need to clear everything outside of it and enable // the GL scissor so we don't draw anything where we shouldn't // enable scissor for this frame const uint32_t height = hw->getHeight(); engine.setScissor(scissor.left, height - scissor.bottom, scissor.getWidth(), scissor.getHeight()); } } } /* * and then, render the layers targeted at the framebuffer */ const Vector< sp<Layer> >& layers(hw->getVisibleLayersSortedByZ()); const size_t count = layers.size(); const Transform& tr = hw->getTransform(); if (cur != end) { // we're using h/w composer #ifdef QCOM_BSP int fbWidth= hw->getWidth(); int fbHeight= hw->getHeight(); /* if GPUTileRender optimization property is on & can be used * i) Enable EGL_SWAP_PRESERVED flag * ii) do startTile with union DirtyRect * else , Disable EGL_SWAP_PRESERVED */ if(mGpuTileRenderEnable && (mDisplays.size()==1)) { if(mCanUseGpuTileRender && !mUnionDirtyRect.isEmpty()) { hw->eglSwapPreserved(true); Rect dr = mUnionDirtyRect; engine.startTileComposition(dr.left, (fbHeight-dr.bottom), (dr.right-dr.left), (dr.bottom-dr.top), 0); } else { // Un Set EGL_SWAP_PRESERVED flag, if no tiling required. hw->eglSwapPreserved(false); } // DrawWormHole/Any Draw has to be within startTile & EndTile if (hasGlesComposition) { if (hasHwcComposition) { if(mCanUseGpuTileRender && !mUnionDirtyRect.isEmpty()) { const Rect& scissor(mUnionDirtyRect); engine.setScissor(scissor.left, hw->getHeight()- scissor.bottom, scissor.getWidth(), scissor.getHeight()); engine.clearWithColor(0, 0, 0, 0); engine.disableScissor(); } else { engine.clearWithColor(0, 0, 0, 0); } } else { if (cur->getCompositionType() != HWC_BLIT && !clearRegion.isEmpty()) { drawWormhole(hw, clearRegion); } } } } #endif for (size_t i=0 ; i<count && cur!=end ; ++i, ++cur) { const sp<Layer>& layer(layers[i]); const Region clip(dirty.intersect(tr.transform(layer->visibleRegion))); if (!clip.isEmpty()) { switch (cur->getCompositionType()) { case HWC_CURSOR_OVERLAY: case HWC_OVERLAY: { const Layer::State& state(layer->getDrawingState()); if ((cur->getHints() & HWC_HINT_CLEAR_FB) && i && layer->isOpaque(state) && (state.alpha == 0xFF) && hasGlesComposition) { // never clear the very first layer since we're // guaranteed the FB is already cleared layer->clearWithOpenGL(hw, clip); } break; } case HWC_FRAMEBUFFER: { layer->draw(hw, clip); break; } case HWC_BLIT: //Do nothing break; case HWC_FRAMEBUFFER_TARGET: { // this should not happen as the iterator shouldn't // let us get there. ALOGW("HWC_FRAMEBUFFER_TARGET found in hwc list (index=%zu)", i); break; } } } layer->setAcquireFence(hw, *cur); } #ifdef QCOM_BSP // call EndTile, if starTile has been called in this cycle. if(mGpuTileRenderEnable && (mDisplays.size()==1)) { if(mCanUseGpuTileRender && !mUnionDirtyRect.isEmpty()) { engine.endTileComposition(GL_PRESERVE); } } #endif } else { // we're not using h/w composer for (size_t i=0 ; i<count ; ++i) { const sp<Layer>& layer(layers[i]); const Region clip(dirty.intersect( tr.transform(layer->visibleRegion))); if (!clip.isEmpty()) { layer->draw(hw, clip); } } } // disable scissor at the end of the frame engine.disableScissor(); return true; } doComposeSurfaces依次分析:hasGlesComposition需要Open GL來合成的layer,hasHwcComposition需要HWC來合成的layer。
這2各變量不是互斥的,有同時存在需要Open GL layer & HWC layer。
hasHwcComposition在2種情況下是true。
1)layer的類型是HWC_Framebuffer的時候,通常情況下是true。
2)cur ==end 核心實現layer->draw 來完成。
3)cur!=end, 將有hwc來實現。
{ ATRACE_CALL(); if (CC_UNLIKELY(mActiveBuffer == 0)) { // the texture has not been created yet, this Layer has // in fact never been drawn into. This happens frequently with // SurfaceView because the WindowManager can't know when the client // has drawn the first time. // If there is nothing under us, we paint the screen in black, otherwise // we just skip this update. // figure out if there is something below us Region under; const SurfaceFlinger::LayerVector& drawingLayers( mFlinger->mDrawingState.layersSortedByZ); const size_t count = drawingLayers.size(); for (size_t i=0 ; i<count ; ++i) { const sp<Layer>& layer(drawingLayers[i]); if (layer.get() == static_cast<Layer const*>(this)) break; under.orSelf( hw->getTransform().transform(layer->visibleRegion) ); } // if not everything below us is covered, we plug the holes! Region holes(clip.subtract(under)); if (!holes.isEmpty()) { clearWithOpenGL(hw, holes, 0, 0, 0, 1); } return; } // Bind the current buffer to the GL texture, and wait for it to be // ready for us to draw into. status_t err = mSurfaceFlingerConsumer->bindTextureImage(); if (err != NO_ERROR) { ALOGW("onDraw: bindTextureImage failed (err=%d)", err); // Go ahead and draw the buffer anyway; no matter what we do the screen // is probably going to have something visibly wrong. } bool blackOutLayer = isProtected() || (isSecure() && !hw->isSecure()); RenderEngine& engine(mFlinger->getRenderEngine()); if (!blackOutLayer) { // TODO: we could be more subtle with isFixedSize() const bool useFiltering = getFiltering() || needsFiltering(hw) || isFixedSize(); // Query the texture matrix given our current filtering mode. float textureMatrix[16]; mSurfaceFlingerConsumer->setFilteringEnabled(useFiltering); mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix); if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) { /* * the code below applies the display's inverse transform to the texture transform */ // create a 4x4 transform matrix from the display transform flags const mat4 flipH(-1,0,0,0, 0,1,0,0, 0,0,1,0, 1,0,0,1); const mat4 flipV( 1,0,0,0, 0,-1,0,0, 0,0,1,0, 0,1,0,1); const mat4 rot90( 0,1,0,0, -1,0,0,0, 0,0,1,0, 1,0,0,1); mat4 tr; uint32_t transform = hw->getOrientationTransform(); if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) tr = tr * rot90; if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) tr = tr * flipH; if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) tr = tr * flipV; // calculate the inverse tr = inverse(tr); // and finally apply it to the original texture matrix const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr); memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix)); } // Set things up for texturing. mTexture.setDimensions(mActiveBuffer->getWidth(), mActiveBuffer->getHeight()); mTexture.setFiltering(useFiltering); mTexture.setMatrix(textureMatrix); engine.setupLayerTexturing(mTexture); } else { engine.setupLayerBlackedOut(); } drawWithOpenGL(hw, clip, useIdentityTransform); engine.disableTexturing(); }
裡面關鍵就是drawwithOpenGL,可見是由Open GL來合成layer。
解析BroadcastReceiver之你需要了解的一些東東,broadcastreceiver 前些天把四大組件之一的Service扯了一遍,今天就要開始談談它的弟兄
我的Android進階之旅之Android自定義View來實現解析lrc歌詞同步滾動、上下拖動、縮放歌詞等功能 前言 最近有個項目有關於播放音樂時候
使用新版Android Studio檢測內存洩露和性能 內存洩露,是Android開發者最頭疼的事。可能一處小小的內存洩露,都可能是毀於千裡之堤的蟻穴。 怎麼才能檢測內
Android第三方開源對話消息提示框:SweetAlertDialog(sweet-alert-dialog),Android第三方開源對話消息提示框:SweetAle
伴隨ListView、RecyclerView、ScrollView滾動