1.adb shell dumpsys SurfaceFlinger
dump时会打印出是HWC合成还是GPU合成,androidO开始HWC换成了Device,GPU换成了Client。
2.问题场景
打开 Settings -> Accessibility -> Color inversion 后冷启动应用会出现闪白屏。
在androidO上该问题是原生问题,但是在androidP上不再是原生问题。
通过dump SurfaceFlinger发现问题场景下为Client合成方式,而正常时为Device合成方式。
google原生解释理由:
The reason is that the surface of the app has been created, but the app does not send the data that needs to be displayed to the surfaceflinger in time, and waits for a few frames before starting to update. In this case, the surfaceflinger has no valid data, and it shows black surface. If it is a reverse color(using Invert colours option), we will see a white surface twinkling.
3.分析CompositionType
1) 在prepare的时候会去设置frame data到hwc hal中,会传入compositionType,根据下面的代码可以看出默认是有四种方式的:Sideband、Cursor、Device、SolidColor。
frameworks/native/services/surfaceflinger/BufferLayer.cpp
618void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) {
...
641 // Sideband layers
642 if (getBE().compositionInfo.hwc.sidebandStream.get()) {
643 setCompositionType(hwcId, HWC2::Composition::Sideband);
644 ALOGV("[%s] Requesting Sideband composition", mName.string());
645 error = hwcLayer->setSidebandStream(getBE().compositionInfo.hwc.sidebandStream->handle());
646 if (error != HWC2::Error::None) {
647 ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(),
648 getBE().compositionInfo.hwc.sidebandStream->handle(), to_string(error).c_str(),
649 static_cast<int32_t>(error));
650 }
651 return;
652 }
653
654 // Device or Cursor layers
655 if (mPotentialCursor) {
656 ALOGV("[%s] Requesting Cursor composition", mName.string());
657 setCompositionType(hwcId, HWC2::Composition::Cursor);
658 } else {
659 ALOGV("[%s] Requesting Device composition", mName.string());
660 setCompositionType(hwcId, HWC2::Composition::Device);
661 }
frameworks/native/services/surfaceflinger/ColorLayer.cpp
64void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) {
...
78 setCompositionType(hwcId, HWC2::Composition::SolidColor);
还有一个地方会强制设置compositionType为Client。
hwc Hal层的layer创建失败;禁用HWC;打开debugRegion;layer的一些特殊属性等会设置compositionType为Client到hwc hal中。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
1959void SurfaceFlinger::setUpHWComposer() {
...
2003 if (!layer->hasHwcLayer(hwcId)) {
2004 if (!layer->createHwcLayer(getBE().mHwc.get(), hwcId)) {
2005 layer->forceClientComposition(hwcId);
2006 continue;
2007 }
2008 }
2009
2010 layer->setGeometry(displayDevice, i);
2011 if (mDebugDisableHWC || mDebugRegion) {
2012 layer->forceClientComposition(hwcId);
2013 }
...
2033 for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
2034 if (layer->isHdrY410()) {
2035 layer->forceClientComposition(hwcId);
2036 } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
2037 layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
2038 !displayDevice->hasHDR10Support()) {
2039 layer->forceClientComposition(hwcId);
2040 } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
2041 layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
2042 !displayDevice->hasHLGSupport()) {
2043 layer->forceClientComposition(hwcId);
2044 }
2045
2046 if (layer->getForceClientComposition(hwcId)) {
2047 ALOGV("[%s] Requesting Client composition", layer->getName().string());
2048 layer->setCompositionType(hwcId, HWC2::Composition::Client);
2049 continue;
2050 }
那么我们分析下Layer的setCompositionType,会根据callIntoHwc去决定是否将compositionType设置到hwc hal中,surfaceflinger第一次会向hwc hal中设置compositionType(callIntoHwc为true),hwc hal会返回给surfaceflinger侧compositionType改变的值(androidO上新增mChangeType),如果有改变的话只需要改变surfaceflinger侧即可,无需往hwc hal再设置compositionType(callIntoHwc为false),这个后面会分析。
frameworks/native/services/surfaceflinger/Layer.cpp
730void Layer::setCompositionType(int32_t hwcId, HWC2::Composition type, bool callIntoHwc) {
731 if (getBE().mHwcLayers.count(hwcId) == 0) {
732 ALOGE("setCompositionType called without a valid HWC layer");
733 return;
734 }
735 auto& hwcInfo = getBE().mHwcLayers[hwcId];
736 auto& hwcLayer = hwcInfo.layer;
737 ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", hwcLayer->getId(), to_string(type).c_str(),
738 static_cast<int>(callIntoHwc));
739 if (hwcInfo.compositionType != type) {
740 ALOGV(" actually setting");
741 hwcInfo.compositionType = type;
742 if (callIntoHwc) {
743 auto error = hwcLayer->setCompositionType(type);
744 ALOGE_IF(error != HWC2::Error::None,
745 "[%s] Failed to set "
746 "composition type %s: %s (%d)",
747 mName.string(), to_string(type).c_str(), to_string(error).c_str(),
748 static_cast<int32_t>(error));
749 }
750 }
751}
最后,分析下hwc hal返回的结果。
在hwc hal层结束了prepare操作后,surfaceflinger侧会调用getChangedCompositionTypes得到底层更改后的compositionType,hwc hal层会实现getChangedCompositionTypes方法,当有改变的话changedTypes.count(hwcLayer)不为0,这样的话会向layer中重新设置compositionType,但是此时的callIntoHwc为false,即不向hwc hal层中设置。
同时,HWComposer中通过DisplayDate保存compositionType,注意只有两种:hasClientComposition、hasDeviceComposition。
frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
406status_t HWComposer::prepare(DisplayDevice& displayDevice) {
...
456 error = hwcDisplay->validate(&numTypes, &numRequests);
463 std::unordered_map<HWC2::Layer*, HWC2::Composition> changedTypes;
464 changedTypes.reserve(numTypes);
465 error = hwcDisplay->getChangedCompositionTypes(&changedTypes);
477 for (auto& layer : displayDevice.getVisibleLayersSortedByZ()) {
478 auto hwcLayer = layer->getHwcLayer(displayId);
479
480 if (changedTypes.count(hwcLayer) != 0) {
481 // We pass false so we only update our state and don't call back
482 // into the HWC device
483 validateChange(layer->getCompositionType(displayId),
484 changedTypes[hwcLayer]);
485 layer->setCompositionType(displayId, changedTypes[hwcLayer], false);
486 }
488 switch (layer->getCompositionType(displayId)) {
489 case HWC2::Composition::Client:
490 displayData.hasClientComposition = true;
491 break;
492 case HWC2::Composition::Device:
493 case HWC2::Composition::SolidColor:
494 case HWC2::Composition::Cursor:
495 case HWC2::Composition::Sideband:
496 displayData.hasDeviceComposition = true;
497 break;
498 default:
499 break;
500 }
4.结论
有些平台hwc可以很好的支持这种场景的合成,而有些平台不支持的话就出现了问题。
至于底层如何实现的,这里就不方便贴出来了,自行研究~
网友评论