整体流程
-
一个 Window 有一个 viewtree 对应有一个 surface ,Surface 实现 Parcelable
-
把 Window 上的 viewtree 也就是 surface 传递给 SurfaceFlinger 去的合成, 内存共享的方式 ”GraphicBuffer“ 不能使用 Binder , 占用内存空间比较大.
-
SurfaceFlinger 是把多个 surface 合成一个 surface
-
SurfaceFlinger 合成一个 surface 时需要垂直同步信号 vsyce 配合使用,如果没有处理好信号同步,会出现闪屏、黑屏、断层;
-
从 SurfaceFlinger 到底层驱动硬件也有一个同步信号 fence ,俗称栅栏
细化流程图
view -> surface -> bufferQueue -> surfaceFlinger -> Layar -> Display
SurfaceFlinger 五大定律
第一定律
SurfaceFlinger是整个Android系统渲染的核心进程。所有应用的渲染逻辑最终都会来到SurfaceFlinger中进行处理,最终会把处理后的图像数据交给CPU或者GPU进行绘制。
第二定律
在每一个应用中都以Surface作为一个图元传递单元,向SurfaceFlinger这个服务端传递图元数据。
第三定律
SurfaceFlinger是以生产者以及消费者为核心设计思想,把每一个应用进程作为生产者生产图元保存到SurfaceFlinger的图元队列中,SurfaceFlinger则作为消费者依照一定的规则把生产者存放到SurfaceFlinger中的队列一一处理。
第四定律
为了能够跨进程的传输大容量的图元数据,使用了匿名共享内存内存作为工具把图元数据都输送到SurfaceFlinger中处理。
第五定律
SurfaceFlinger底层有一个时间钟在不断的循环,或从硬件中断发出,或从软件模拟发出计时唤起,每隔一段时间都会获取SurfaceFlinger中的图元队列通过CPU/GPU绘制在屏幕。
ViewRootImpl.performTraversals() ->
private void performTraversals() {
...
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
...
}
void pokeDrawLockIfNeeded() {
final int displayState = mAttachInfo.mDisplayState;
if (mView != null && mAdded && mTraversalScheduled
&& (displayState == Display.STATE_DOZE
|| displayState == Display.STATE_DOZE_SUSPEND)) {
try {
mWindowSession.pokeDrawLock(mWindow);
} catch (RemoteException ex) {
// System server died, oh well.
}
}
}
mWindowSession.relayout 传递一个帧 Surface 到 WMS
mSurfaceControl 控制 surface frameNumber = mSurface.getNextFrameNumber();
//frameNumber = mSurface.getNextFrameNumber();
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
float appScale = mAttachInfo.mApplicationScale;
boolean restore = false;
if (params != null && mTranslator != null) {
restore = true;
params.backup();
mTranslator.translateWindowLayout(params);
}
if (params != null) {
if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
if (mOrigWindowType != params.type) {
// For compatibility with old apps, don't crash here.
if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Slog.w(mTag, "Window type can not be changed after "
+ "the window is added; ignoring change of " + mView);
params.type = mOrigWindowType;
}
}
}
long frameNumber = -1;
if (mSurface.isValid()) {
frameNumber = mSurface.getNextFrameNumber();
}
int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
mTempControls, mSurfaceSize, mBlastSurfaceControl);
if (mSurfaceControl.isValid()) {
if (!useBLAST()) {
mSurface.copyFrom(mSurfaceControl);
} else {
final Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x,
mSurfaceSize.y);
// If blastSurface == null that means it hasn't changed since the last time we
// called. In this situation, avoid calling transferFrom as we would then
// inc the generation ID and cause EGL resources to be recreated.
if (blastSurface != null) {
mSurface.transferFrom(blastSurface);
}
}
} else {
destroySurface();
}
mPendingAlwaysConsumeSystemBars =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
if (restore) {
params.restore();
}
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame);
}
setFrame(mTmpFrame);
mInsetsController.onStateChanged(mTempInsets);
mInsetsController.onControlsChanged(mTempControls);
return relayoutResult;
}
AMS.relayoutWindow
public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, Rect outFrame, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
SurfaceControl outBLASTSurfaceControl) {
Arrays.fill(outActiveControls, null);
int result = 0;
boolean configChanged;
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
final WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
final DisplayContent displayContent = win.getDisplayContent();
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
WindowStateAnimator winAnimator = win.mWinAnimator;
if (viewVisibility != View.GONE) {
win.setRequestedSize(requestedWidth, requestedHeight);
}
win.setFrameNumber(frameNumber);
final DisplayContent dc = win.getDisplayContent();
if (!dc.mWaitingForConfig) {
win.finishSeamlessRotation(false /* timeout */);
}
if (win.useBLASTSync()) {
result |= RELAYOUT_RES_BLAST_SYNC;
}
int attrChanges = 0;
int flagChanges = 0;
int privateFlagChanges = 0;
if (attrs != null) {
displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
win.mToken.adjustWindowParams(win, attrs);
// if they don't have the permission, mask out the status bar bits
if (seq == win.mSeq) {
int systemUiVisibility = attrs.systemUiVisibility
| attrs.subtreeSystemUiVisibility;
if ((systemUiVisibility & DISABLE_MASK) != 0) {
if (!hasStatusBarPermission(pid, uid)) {
systemUiVisibility &= ~DISABLE_MASK;
}
}
win.mSystemUiVisibility = systemUiVisibility;
}
if (win.mAttrs.type != attrs.type) {
throw new IllegalArgumentException(
"Window type can not be changed after the window is added.");
}
// Odd choice but less odd than embedding in copyFrom()
if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)
!= 0) {
attrs.x = win.mAttrs.x;
attrs.y = win.mAttrs.y;
attrs.width = win.mAttrs.width;
attrs.height = win.mAttrs.height;
}
flagChanges = win.mAttrs.flags ^ attrs.flags;
privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
attrChanges = win.mAttrs.copyFrom(attrs);
if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
| WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
win.mLayoutNeeded = true;
}
if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
|| (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
win.mActivityRecord.checkKeyguardFlagsChanged();
}
if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
&& (mAccessibilityController != null)) {
// No move or resize, but the controller checks for title changes as well
mAccessibilityController.onSomeWindowResizedOrMovedLocked(
win.getDisplayContent().getDisplayId());
}
if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
updateNonSystemOverlayWindowsVisibilityIfNeeded(
win, win.mWinAnimator.getShown());
}
if ((attrChanges & (WindowManager.LayoutParams.PRIVATE_FLAGS_CHANGED)) != 0) {
winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
}
}
if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
+ " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
winAnimator.mAlpha = attrs.alpha;
}
win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
if (win.mAttrs.surfaceInsets.left != 0
|| win.mAttrs.surfaceInsets.top != 0
|| win.mAttrs.surfaceInsets.right != 0
|| win.mAttrs.surfaceInsets.bottom != 0) {
winAnimator.setOpaqueLocked(false);
}
final int oldVisibility = win.mViewVisibility;
// If the window is becoming visible, visibleOrAdding may change which may in turn
// change the IME target.
final boolean becameVisible =
(oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
&& viewVisibility == View.VISIBLE;
boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
|| becameVisible;
boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
if ((flagChanges & FLAG_SECURE) != 0 && winAnimator.mSurfaceController != null) {
winAnimator.mSurfaceController.setSecure(win.isSecureLocked());
}
win.mRelayoutCalled = true;
win.mInRelayout = true;
win.setViewVisibility(viewVisibility);
ProtoLog.i(WM_DEBUG_SCREEN_ON,
"Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility,
viewVisibility, new RuntimeException().fillInStackTrace());
win.setDisplayLayoutNeeded();
win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
// We should only relayout if the view is visible, it is a starting window, or the
// associated appToken is not hidden.
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
|| win.mActivityRecord.isClientVisible());
// If we are not currently running the exit animation, we need to see about starting
// one.
// We don't want to animate visibility of windows which are pending replacement.
// In the case of activity relaunch child windows could request visibility changes as
// they are detached from the main application window during the tear down process.
// If we satisfied these visibility changes though, we would cause a visual glitch
// hiding the window before it's replacement was available. So we just do nothing on
// our side.
// This must be called before the call to performSurfacePlacement.
if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) {
if (DEBUG_VISIBILITY) {
Slog.i(TAG_WM,
"Relayout invis " + win + ": mAnimatingExit=" + win.mAnimatingExit);
}
result |= RELAYOUT_RES_SURFACE_CHANGED;
if (!win.mWillReplaceWindow) {
focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange);
}
}
// We may be deferring layout passes at the moment, but since the client is interested
// in the new out values right now we need to force a layout.
mWindowPlacerLocked.performSurfacePlacement(true /* force */);
if (shouldRelayout) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
result = win.relayoutVisibleWindow(result, attrChanges);
try {
result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,
result, win, winAnimator);
} catch (Exception e) {
displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
ProtoLog.w(WM_ERROR,
"Exception thrown when creating surface for client %s (%s). %s",
client, win.mAttrs.getTitle(), e);
Binder.restoreCallingIdentity(origId);
return 0;
}
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = true;
}
if (win.mAttrs.type == TYPE_INPUT_METHOD
&& displayContent.mInputMethodWindow == null) {
displayContent.setInputMethodWindowLocked(win);
imMayMove = true;
}
win.adjustStartingWindowFlags();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} else {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_2");
winAnimator.mEnterAnimationPending = false;
winAnimator.mEnteringAnimation = false;
if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
// We already told the client to go invisible, but the message may not be
// handled yet, or it might want to draw a last frame. If we already have a
// surface, let the client use that, but don't create new surface at this point.
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
winAnimator.mSurfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} else {
if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
+ win.mAttrs.getTitle());
outSurfaceControl.release();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (focusMayChange) {
if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
imMayMove = false;
}
}
// updateFocusedWindowLocked() already assigned layers so we only need to
// reassign them at this point if the IM window state gets shuffled
boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
if (imMayMove) {
displayContent.computeImeTarget(true /* updateImeTarget */);
if (toBeDisplayed) {
// Little hack here -- we -should- be able to rely on the function to return
// true if the IME has moved and needs its layer recomputed. However, if the IME
// was hidden and isn't actually moved in the list, its layer may be out of data
// so we make sure to recompute it.
displayContent.assignWindowLayers(false /* setLayoutNeeded */);
}
}
if (wallpaperMayMove) {
displayContent.pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
if (win.mActivityRecord != null) {
displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mActivityRecord);
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientation");
configChanged = displayContent.updateOrientation();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (toBeDisplayed && win.mIsWallpaper) {
displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
}
if (win.mActivityRecord != null) {
win.mActivityRecord.updateReportedVisibilityLocked();
}
if (winAnimator.mReportSurfaceResized) {
winAnimator.mReportSurfaceResized = false;
result |= WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED;
}
if (displayPolicy.areSystemBarsForcedShownLw(win)) {
result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
}
if (!win.isGoneForLayoutLw()) {
win.mResizedWhileGone = false;
}
// We must always send the latest {@link MergedConfiguration}, regardless of whether we
// have already reported it. The client might not have processed the previous value yet
// and needs process it before handling the corresponding window frame. the variable
// {@code mergedConfiguration} is an out parameter that will be passed back to the
// client over IPC and checked there.
// Note: in the cases where the window is tied to an activity, we should not send a
// configuration update when the window has requested to be hidden. Doing so can lead
// to the client erroneously accepting a configuration that would have otherwise caused
// an activity restart. We instead hand back the last reported
// {@link MergedConfiguration}.
if (shouldRelayout) {
win.getMergedConfiguration(mergedConfiguration);
} else {
win.getLastReportedMergedConfiguration(mergedConfiguration);
}
win.setLastReportedMergedConfiguration(mergedConfiguration);
// Update the last inset values here because the values are sent back to the client.
// The last inset values represent the last client state
win.updateLastInsetValues();
win.getCompatFrame(outFrame);
win.getInsetsForRelayout(outContentInsets, outVisibleInsets,
outStableInsets);
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
outInsetsState.set(win.getInsetsState(), win.isClientLocal());
if (DEBUG) {
Slog.v(TAG_WM, "Relayout given client " + client.asBinder()
+ ", requestedWidth=" + requestedWidth
+ ", requestedHeight=" + requestedHeight
+ ", viewVisibility=" + viewVisibility
+ "\nRelayout returning frame=" + outFrame
+ ", surface=" + outSurfaceControl);
}
ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
win, focusMayChange);
result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
if (DEBUG_LAYOUT) {
Slog.v(TAG_WM,
"Relayout complete " + win + ": outFrame=" + outFrame.toShortString());
}
win.mInRelayout = false;
if (configChanged) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"relayoutWindow: postNewConfigurationToHandler");
displayContent.sendNewConfiguration();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (winAnimator.mSurfaceController != null) {
outSurfaceSize.set(winAnimator.mSurfaceController.getWidth(),
winAnimator.mSurfaceController.getHeight());
}
getInsetsSourceControls(win, outActiveControls);
}
Binder.restoreCallingIdentity(origId);
return result;
}