Android O 硬鼠导致黑屏
- Sprite Surface 创建
鼠标在Android中被称为了Sprite,其Surface的创建是在SpriteController.cpp,目录/frameworks/base/services/input/SpriteController.cpp,那么Sprite对应的图片在哪儿呢,肯定有一个地方会去加载对应分辨率的cursor图片;由frameworks/base/core/java/android/view/PointerIcon.java中的getSystemIcon()导入,然后再由SpriteController::SpriteImpl::setIcon(),设置为Sprite这个精灵的资源。每次鼠标移动后,InputReader线程总会获取这个坐标值,然后InputDispatch会分发给WindowManagerService,而ViewRootImpl会读取等等
鼠标也是Pointer,在input目录下面还有一个PointerController,这个应该是控制鼠标的,继承自PointerControllerInterface
/** * Interface for tracking a mouse / touch pad pointer and touch pad spots. * * The spots are sprites on screen that visually represent the positions of * fingers * * The pointer controller is responsible for providing synchronization and for tracking * display orientation changes if needed. */class PointerControllerInterface : public virtual RefBase {
更新鼠标及创建Surface的地方是在SpriteController的SpriteController::doUpdateSprites()中
void SpriteController::doUpdateSprites() { // Collect information about sprite updates. // Each sprite update record includes a reference to its associated sprite so we can // be certain the sprites will not be deleted while this function runs. Sprites // may invalidate themselves again during this time but we will handle those changes // in the next iteration. Vector updates; size_t numSprites; { // acquire lock AutoMutex _l(mLock); numSprites = mLocked.invalidatedSprites.size(); for (size_t i = 0; i < numSprites; i++) { const sp& sprite = mLocked.invalidatedSprites.itemAt(i); updates.push(SpriteUpdate(sprite, sprite->getStateLocked())); sprite->resetDirtyLocked(); } mLocked.invalidatedSprites.clear(); } // release lock // Create missing surfaces. bool surfaceChanged = false; for (size_t i = 0; i < numSprites; i++) { SpriteUpdate& update = updates.editItemAt(i); if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) { update.state.surfaceWidth = update.state.icon.bitmap.width(); update.state.surfaceHeight = update.state.icon.bitmap.height(); update.state.surfaceDrawn = false; update.state.surfaceVisible = false; update.state.surfaceControl = obtainSurface( update.state.surfaceWidth, update.state.surfaceHeight); if (update.state.surfaceControl != NULL) { update.surfaceChanged = surfaceChanged = true; } } } // Resize sprites if needed, inside a global transaction. bool haveGlobalTransaction = false; for (size_t i = 0; i < numSprites; i++) { SpriteUpdate& update = updates.editItemAt(i); if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) { int32_t desiredWidth = update.state.icon.bitmap.width(); int32_t desiredHeight = update.state.icon.bitmap.height(); if (update.state.surfaceWidth < desiredWidth || update.state.surfaceHeight < desiredHeight) { if (!haveGlobalTransaction) { SurfaceComposerClient::openGlobalTransaction(); haveGlobalTransaction = true; } status_t status = update.state.surfaceControl->setSize(desiredWidth, desiredHeight); if (status) { ALOGE("Error %d resizing sprite surface from %dx%d to %dx%d", status, update.state.surfaceWidth, update.state.surfaceHeight, desiredWidth, desiredHeight); } else { update.state.surfaceWidth = desiredWidth; update.state.surfaceHeight = desiredHeight; update.state.surfaceDrawn = false; update.surfaceChanged = surfaceChanged = true; if (update.state.surfaceVisible) { status = update.state.surfaceControl->hide(); if (status) { ALOGE("Error %d hiding sprite surface after resize.", status); } else { update.state.surfaceVisible = false; } } } } } } if (haveGlobalTransaction) { SurfaceComposerClient::closeGlobalTransaction(); }
看到 update.state.surfaceControl = obtainSurface(update.state.surfaceWidth, update.state.surfaceHeight); 其实就是创建了一个surface,在一开始进入的主界面的时候,鼠标是没有的,当移动了下鼠标或者点击了下鼠标后就执行这个创建了这个surface
- 黑屏分析
关于Android Graphic的渲染显示的总体流程就不细讲了,因为知识点太多无法展开
Android SurfaceFlinger与HWC的配合来实现硬鼠,也就是说鼠标不是用GPU画上去,而是由hwcomposer来处理的;当APP将rawdata放入GraphicBuffer后,SurfaceFlinger会询问hwcomposer(在prepare阶段实现),对于此Layer(Layer是跟Surface一一对应的)你hwcomposer处理不,若处理的话就将CompositionType置为OVERLAY(限于公司内部实现的hwcomposer,在此不贴源代码了),这样在SurfaceFlinger的doComposeSurface阶段就会将OVERLAY的Layer不再调用GPU去画了
bool SurfaceFlinger::doComposeSurfaces(const sp& 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); bool hasGlesComposition = hwc.hasGlesComposition(id);//当前的Display是否有GLES合成的Layer 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 const bool hasHwcComposition = hwc.hasHwcComposition(id); 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. engine.clearWithColor(0, 0, 0, 0);//将back FrameBuffer全部清空 } 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 if (!region.isEmpty()) { // can happen with SurfaceView drawWormhole(hw, region);//将back FrameBuffer对应的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 >& layers(hw->getVisibleLayersSortedByZ()); const size_t count = layers.size(); const Transform& tr = hw->getTransform(); if (cur != end) { // we're using h/w composer for (size_t i=0 ; i& 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);//GPU去画的时候,要将OVERLAY区域挖洞,以免将其遮挡,显示不出来 } break; } case HWC_FRAMEBUFFER: { layer->draw(hw, clip);//GPU去画对应的Layer break; } case HWC_FRAMEBUFFER_TARGET: {//GPU最终渲染的目的地,可以理解为就是back FrameBuffer,保存合成后的结果的 // 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); } } else { // we're not using h/w composer for (size_t i=0 ; i& 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;}
走硬鼠时,dumpsys SurfaceFlinger信息如下
[20181213_15:30:57] h/w composer state:[20181213_15:30:57] h/w composer present and enabled[20181213_15:30:57] Hardware Composer state (version 01010000):[20181213_15:30:57] mDebugForceFakeVSync=0[20181213_15:30:57] Display[0] configurations (* current):[20181213_15:30:57] * 0: 1920x1080, xdpi=159.895004, ydpi=160.421005, refresh=16666666, colorMode=0[20181213_15:30:57] numHwLayers=3, flags=00000000[20181213_15:30:57] type | handle | hint | flag | tr | blnd | format | source crop (l,t,r,b) | frame | name [20181213_15:30:57] -----------+----------+------+------+----+------+-------------+--------------------------------+------------------------+------[20181213_15:30:57] HWC | ad808c40 | 0002 | 0000 | 00 | 0100 | RGBx_8888 | 0, 0, 1, 1 | 0, 0, 1920, 1080 | SurfaceView - com.mych.haixin.rude/com.mych.cloudgameclient.main.activity.StartUpActivity#0[20181213_15:30:57] HWC | adb8f3c0 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0, 0, 44, 56 | 375, 584, 419, 640 | Sprite#0[20181213_15:30:57] FB TARGET | adeca6e0 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0, 0, 1920, 1080 | 0, 0, 1920, 1080 | HWC_FRAMEBUFFER_TARGET
可以看到此时参加Compose的Layer中并不存在需要GLES合成的Layer
那将我们添加的硬鼠逻辑去掉,让其走由GPU去画,这种情况下并没有黑帧遮住,其对应的dumpsys SurfaceFlinger信息如下
h/w composer state: h/w composer present and enabledHardware Composer state (version 01010000): mDebugForceFakeVSync=0 Display[0] configurations (* current): * 0: 1920x1080, xdpi=159.895004, ydpi=160.421005, refresh=16666666, colorMode=0 numHwLayers=3, flags=00000000 type | handle | hint | flag | tr | blnd | format | source crop (l,t,r,b) | frame | name -----------+----------+------+------+----+------+-------------+--------------------------------+------------------------+------ HWC | a80292c0 | 0002 | 0000 | 00 | 0100 | RGBx_8888 | 0, 0, 1, 1 | 0, 0, 1920, 1080 | SurfaceView - com.mych.haixin.rude/com.mych.cloudgameclient.main.activity.StartUpActivity#0 GLES | a80248c0 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0, 0, 44, 56 | 388, 631, 432, 687 | Sprite#0 FB TARGET | a85c95a0 | 0000 | 0000 | 00 | 0105 | RGBA_8888 | 0, 0, 1920, 1080 | 0, 0, 1920, 1080 | HWC_FRAMEBUFFER_TARGET
这就比较奇怪了,不管走不走硬鼠,在这种case下都是只有两个Layer参与合成,那黑帧是哪个Layer造成的呢
而且在hwcomposer中将参与合成Layer的texture Buffer全部dump出来,也不存在黑帧,但dump出的FrameBufferTarget中是有黑帧的,这个黑帧其实是SurfaceView的Background-surfaceView,是本身就应该出现的,因为在SurfaceView的sidebandstream到来之前,需要拿黑屏遮一下,开始播放后,background-surfaceView就不会参与合成的
怀疑点1:是不是硬鼠条件下没有去清FrameBuffer,因为此时参与合成的Layer的compositionType都是OVERLAY,doComposeSurfaces中的hasGlesComposition为false,合成之前就不会去清FrameBuffer了,添加如下code,强制去清一次
}else 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. ALOGE("hasHwcComposition else if engine.clearWithColor(0, 0, 0, 0);"); engine.clearWithColor(0, 0, 0, 0); }
测试结果是当硬鼠显示时依旧会有黑屏发生,发生的时长就是硬鼠存在的时间(因为鼠标不动的话,过一段时间会自动消失),硬鼠消失时,黑屏也消失了,透出了后面显示的内容。
奇怪了,不管当前的Layer是什么属性我都去清FrameBuffer了,黑帧出现了,我也已经清掉了啊,难道后面又画上去了,如果后面画上去的话,应该可以dump到这个黑帧
怀疑点2;是不是我虽然请了FrameBuffer(back FrameBuffer),但并没有执行swap,也就是说,你虽然清了,但显示时还是以前的Front FrameBuffer
void DisplayDevice::swapBuffers(HWComposer& hwc) const {#ifdef USE_HWC2 if (hwc.hasClientComposition(mHwcDisplayId)) {#else // We need to call eglSwapBuffers() if: // (1) we don't have a hardware composer, or // (2) we did GLES composition this frame, and either // (a) we have framebuffer target support (not present on legacy // devices, where HWComposer::commit() handles things); or // (b) this is a virtual display if (hwc.initCheck() != NO_ERROR || (hwc.hasGlesComposition(mHwcDisplayId) && (hwc.supportsFramebufferTarget() || mType >= DISPLAY_VIRTUAL))) {#endif EGLBoolean success = eglSwapBuffers(mDisplay, mSurface); ALOGE("eglSwapBuffers(%p, %p)", mDisplay, mSurface); if (!success) { EGLint error = eglGetError(); if (error == EGL_CONTEXT_LOST || mType == DisplayDevice::DISPLAY_PRIMARY) { LOG_ALWAYS_FATAL("eglSwapBuffers(%p, %p) failed with 0x%08x", mDisplay, mSurface, error); } else { ALOGE("eglSwapBuffers(%p, %p) failed with 0x%08x", mDisplay, mSurface, error); } } } status_t result = mDisplaySurface->advanceFrame(); if (result != NO_ERROR) { ALOGE("[%s] failed pushing new frame to HWC: %d", mDisplayName.string(), result); }}
DisplayDevice调用eglswapBuffer的地方,有个条件判断
(hwc.hasGlesComposition(mHwcDisplayId),这个是false的,因为我们参与合成Layer中并没有GLES的Layer
将这个条件注释掉,测试发现黑屏的确不存在了,也就是说,当Layer中没有GPU(GLES)参与的条件下,没有执行swap去调用底层的OSD执行pandisplay去交换Front FrameBuffer。
那这种修改会不会有效率上的损失呢,按照SurfaceFlinger的设计,我只管GLES的合成,对HWC的CompositionType的去交给hwcomposer去控管,hwc又不能控制GPU去,让hwc去控制OSD去显示,虽然这样也可以做到,但不建议让hwc去通过driver直接去控,光时序问题就会将你整蒙的,在SurfaceFlinger中修改是最好的,因为大多数情况下SurfaceFlinger的每次合成都会去清FrameBuffer,在这种case下去清并swap并不会有performance上的损失的
另一种更简单的修改就是不要什么硬鼠了,其实鼠标那个小图标占不了多少GPU资源的(对于普通机型来说)
Android O中的hwc 1.1对于Cursor的支持不是很好,但这个在hwc 2.0中得到了解决,在hwc 2.0中直接给出了更新鼠标的接口(hwc2_function_pointer_t) set_cursor_position
结论:当compose的Surface(Layer)中没有Mali要参与的情况下,SurfaceFlinger和hwc都没有去清Framebuffer,但硬鼠的情况下,FrameBuffer显示了上次合成的结果,按照SurfaceFlinger的设计,这种情况我什么都不管了,全部交给hwc处理,但此时因为FrameBuffer并未clear,这样就导致残留和黑屏(其实是上一次的合成结果);这种情况可以在SurfaceFlinger中添加code,去清一下Framebuffer并swap Buffer。
更多相关文章
- Android SplashActivity启动时黑屏的问题
- android 使用TextView/EditText应该注意的地方
- 使用ListView应该注意的地方
- android 程序启动界面的短暂黑屏
- Android避免启动时闪一下黑屏
- Android--ListView滑动时出现黑屏解决方法(整合)
- activity跳转闪现黑屏
- 分享到某个地方
- android activity FLAG_ACTIVITY_CLEAR_TASK 跳转出现短暂的白屏