SurfaceFlinger05-屏幕显示区域更新

1,100 阅读18分钟

前言

在上篇文章《SurfaceFlinger04-默认屏幕加载过程 》中对surfacelinger启动过程中对默认屏幕的加载过程做了分析,本篇文章中将更进一步,对整个屏幕管理机制进行分析和介绍。

在整个Android系统架构的HAL层以上部分,对屏幕的管理由两个模块共同完成:

  • surfaceflinger进程

在整个屏幕管理机制中承上启下,向下直接跟HAL进行通信,通过热插拔事件从HAL层加载硬件物理屏幕,并将来自system_server下发的屏幕状态和配置属性更新给HAL层对应组件。

向上提供了DisplayManager(以下简称DMS)加载屏幕的接口,同时接收DisplayManager下发的屏幕更新参数进行配置的更新。

  • system_server进程

从surfaceflinger中加载物理屏幕信息,创建逻辑屏并与物理屏进行映射,结合应用窗口属性、用户交互操作,对屏幕状态和配置进行更新,并下发给surfaceflinger完成更新。

物理屏(DisplayDevice)是指对硬件物理屏幕的抽象,代表实实在在的物理屏幕,用于描述其物理属性,如尺寸、最大支持亮度、支持分辨率、支持刷新率等。

逻辑屏(LogicalDisplay)更多地是软件层面的封装,侧重于同一个物理屏的不同逻辑设置,受应用和WMS模块影响,如显示区域、显示位置坐标、显示方向等。

每一个物理屏幕都对应一个逻辑屏,可以修改逻辑屏幕参数或更改逻辑屏,做到同一个物理屏的不同显示方式,从而以更灵活的方式处理显示内容。

比如:通过对逻辑屏大小、位置、方向的调整,从而实现屏幕方向、大小、显示区域的变化。

下面将从以下几方面作为切入点,了解整个屏幕显示基本原理:

  1. system_server与surfaceflinger在屏幕管理上的协作方式;
  2. 逻辑屏的更新,包括屏幕方向、大小、显示区域等更新。

一、system_server与surfaceflinger在屏幕管理上的协作方式

surfaceflinger进程中通过热插拔事件从HAL层加载硬件屏幕信息,对于虚拟屏而言,也需要surfaceflinger向HAL层完成注册和创建才能使用。

在system_server进程中,则是通过DMS从surfaceflinger读取屏幕信息,然后创建LogicalDisplay并与之进行映射,并通过LogicalDisplay的不同参数的更新,调整物理屏幕的内容显示方式。

sf-05-01.jpg

1.1、DMS中屏幕的加载方式

DMS中在两个场景下可以进行加载屏幕的操作:DMS启动时和发生热插拔事件时。而加载屏幕的实现,是通过SurfaceControl和DisplayControl这两类中定义的静态方法实现的,如:

// frameworks/base/services/core/java/com/android/server/display/DisplayControl.java

// 从surfaceflinger中获取当前物理屏ID
public static long[] getPhysicalDisplayIds() {
    return nativeGetPhysicalDisplayIds();
}
// 根据物理屏ID从surfaceflinger中获取物理屏Token
public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
    return nativeGetPhysicalDisplayToken(physicalDisplayId);
}

// frameworks/base/core/java/android/view/SurfaceControl.java

// 根据物理屏ID从surfaceflinger中获取物理屏信息
public static StaticDisplayInfo getStaticDisplayInfo(long displayId) {
    return nativeGetStaticDisplayInfo(displayId);
}
// 根据物理屏ID从surfaceflinger中获取物理屏信息
public static DynamicDisplayInfo getDynamicDisplayInfo(long displayId) {
    return nativeGetDynamicDisplayInfo(displayId);
}

DMS加载屏幕过程中,首先会通过DisplayControl.getPhysicalDisplayIds()获得所有物理屏幕ID,然后根据ID加载物理屏Token和屏幕信息,并创建对应的DisplayDevice对象和LogicalDisplay对象,对应方法如下:

// frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java

private void tryConnectDisplayLocked(long physicalDisplayId) {
    // 根据ID获取物理屏token
    final IBinder displayToken =
            mSurfaceControlProxy.getPhysicalDisplayToken(physicalDisplayId);
    if (displayToken != null) {
        // 根据token获取屏幕信息
        SurfaceControl.StaticDisplayInfo staticInfo =
                mSurfaceControlProxy.getStaticDisplayInfo(physicalDisplayId);

        SurfaceControl.DynamicDisplayInfo dynamicInfo =
                mSurfaceControlProxy.getDynamicDisplayInfo(physicalDisplayId);

        SurfaceControl.DesiredDisplayModeSpecs modeSpecs =
                mSurfaceControlProxy.getDesiredDisplayModeSpecs(displayToken);
        LocalDisplayDevice device = mDevices.get(physicalDisplayId);
        
        if (device == null) {
            // Display was added.
            final boolean isFirstDisplay = mDevices.size() == 0;
            // 创建DisplayDevice对象
            device = new LocalDisplayDevice(displayToken, physicalDisplayId, staticInfo,
                    dynamicInfo, modeSpecs, isFirstDisplay);
            mDevices.put(physicalDisplayId, device);
            // 创建LogicalDisplay
            sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
        } else if (device.updateDisplayPropertiesLocked(staticInfo, dynamicInfo,
                modeSpecs)) {
            sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
        }
    }
    ......
}

在获取物理屏ID列表和token时,读取的就是SurfaceFlinger::mPhysicalDisplays中的值:

// frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
    std::vector<PhysicalDisplayId> displayIds;
    ......
    for (const auto& [id, display] : mPhysicalDisplays) {
        if (id != defaultDisplayId) {
            displayIds.push_back(id);
        }
    }
    return displayIds;
}

获得的物理屏标识token作为一个物理屏幕的唯一标识,在屏幕数据更新过程指定唯一的屏幕。

1.2、DMS向surfaceflinger更新屏幕配置

当DMS中逻辑屏信息或屏幕状态发生变化后,通过两种方式将更新内容传递给surfaceflinger中:

  1. 针对屏幕状态、亮度等跟逻辑屏关联不大的属性,通过SurfaceControl中提供的静态方法传递给surfaceflinger;

如:

// frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java

// 更新指定displayToken的Display状态
public void setDisplayPowerMode(IBinder displayToken, int mode) {
    SurfaceControl.setDisplayPowerMode(displayToken, mode);
}

// 更新指定displayToken的Display亮度
public boolean setDisplayBrightness(IBinder displayToken, float brightness) {
    return SurfaceControl.setDisplayBrightness(displayToken, brightness);
}
  1. 和逻辑屏关联较大的属性,如宽高、方向等,在WMS中进行刷新遍历过程中通过Transaction中的方式传递给surfaceflinger;

如:

// frameworks/base/core/java/android/view/SurfaceControl.java

// 更新指定displayToken的显示方向、大小、投影区域
public Transaction setDisplayProjection(IBinder displayToken,
        int orientation, Rect layerStackRect, Rect displayRect) {
    ......
    nativeSetDisplayProjection(mNativeObject, displayToken, orientation,
            layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom,
            displayRect.left, displayRect.top, displayRect.right, displayRect.bottom);
    return this;
}

二、system_server中逻辑屏的更新

在DMS中,DisplayDevice类表示物理屏,LogicalDisplay类表示逻辑屏,DisplayInfo类表示逻辑屏信息。在LogicalDisplay的DisplayInfo更新过程中,通过三个DisplayInfo类对象实现:

  • mBaseDisplayInfo:其中参数都是复用从物理屏幕信息直接引用,可以理解为未经修饰的DisplayInfo;
  • mOverrideDisplayInfo:由WMS根据WMS侧状态更新的DisplayInfo;
  • mInfo:表示当前实际应用的DisplayInfo,mOverrideDisplayInfo对象和mBaseDisplayInfo对象参数的叠加对象,当需要更新逻辑屏时该值将被清空并重新赋值。

LogicalDisplay的DisplayInfo更新受WMS中各种状态的影响,屏幕的大小、方向、像素密度都是根据WMS中的状态进行调整。

比如,WMS中通过监听方向传感器、manifest文件中设置android:screenOrientation属性、Activity中通过setRequestedOrientation(int)方法等方式控制屏幕的方向,就是通过这些参数更改对应逻辑屏的方向,并传递给DMS中更新并应用。

image.png

system_server中一般通过两种方式给DMS更新逻辑屏信息:

  • 方式一:

通过DisplayManagerInternal.setDisplayInfoOverrideFromWindowManager()方法向DMS设置mOverrideDisplayInfo对象,之后将逻辑屏信息填充到Transaction中,传递给surfaceflinger。如更新屏幕方向、大小时:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

private DisplayInfo updateDisplayAndOrientation(Configuration outConfig) {
    ......
    mDisplayInfo.rotation = rotation;
    mDisplayInfo.logicalWidth = dw;
    mDisplayInfo.logicalHeight = dh;
  
    ......
    mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
            mDisplayInfo);

    return mDisplayInfo;
}
  • 方式二:

通过DMS中的内部接口直接设置,之后通过底层SurfaceComposerClient直接调用Binder接口设置给surfaceflinger。

这种方式一般用于更新强依赖物理屏性质的参数,如DisplayMode、亮度、屏幕状态(ON、OFF、DOZE...)等。

更新DisplayMode接口如下:

// frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java

public void setDisplayProperties(int displayId, boolean hasContent,
        float requestedRefreshRate, int requestedMode, float requestedMinRefreshRate,
        float requestedMaxRefreshRate, boolean requestedMinimalPostProcessing,
        boolean disableHdrConversion, boolean inTraversal) {
    setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate,
            requestedMode, requestedMinRefreshRate, requestedMaxRefreshRate,
            requestedMinimalPostProcessing, disableHdrConversion, inTraversal);
}

下面进入具体场景,看下DMS中传递数据,到surfaceflinger收到数据后的完整更新过程。

三、显示区域更新

3.1、DMS.setDisplayProjection()更新逻辑屏区域和实际显示内容区域

屏幕的显示区域,由方向、大小、偏移量等因素决定,DMS中将这些参数通过两个Rect对象进行保存,并通过Transaction.setDisplayProjection()方法传递给了surfaceflinger进程:

// frameworks/base/services/core/java/com/android/server/display/DisplayDevice.java

public final void setProjectionLocked(SurfaceControl.Transaction t, int orientation,
        Rect layerStackRect, Rect displayRect) {
    if (mCurrentOrientation != orientation
            || mCurrentLayerStackRect == null
            || !mCurrentLayerStackRect.equals(layerStackRect)
            || mCurrentDisplayRect == null
            || !mCurrentDisplayRect.equals(displayRect)) {
        mCurrentOrientation = orientation;

        if (mCurrentLayerStackRect == null) {
            mCurrentLayerStackRect = new Rect();
        }
        // 逻辑屏区域大小
        mCurrentLayerStackRect.set(layerStackRect);

        if (mCurrentDisplayRect == null) {
            mCurrentDisplayRect = new Rect();
        }
        // 屏幕实际显示区域大小
        mCurrentDisplayRect.set(displayRect);
        // 通过transaction设置给surfaceflinger进程
        t.setDisplayProjection(mDisplayToken,
                orientation, layerStackRect, displayRect);
    }
}

以上方法中,两个显示区域大小代表的是不同的坐标空间:

  • layerStackRect表示当前逻辑屏的区域大小,属于逻辑屏坐标空间;
  • displayRect表示物理屏的实际显示区域大小,属于物理屏坐标空间。受偏移量、屏幕纵横比影响,会影响layerStackRect的实际显示。

layerStackRect经过变换矩阵变换后投影到displayRect上,最终呈现在屏幕上的显示区域会通过各坐标空间的变换后输出在屏幕坐标空间。

假设surfaceflinger进程中FrameBuffer等其他显示空间保持不变,layerStackRectdisplayRect对内容显示区域影响如下:

  • layerStackRectdisplayRect一致时,最终显示区域一比一输出:

sf-05-03.jpg

  • 如果layerStackRect区域为(0, 0, 1080, 2400),displayRect区域为(100, 100, 1180, 2500)时,即displayRect的X轴和Y轴分别偏移了100,最终显示区域如下:

sf-05-04.jpg

  • 如果layerStackRect区域为(0, 0, 540, 1200),displayRect区域为(0, 0, 1080, 2400)时,即displayRect的X轴和Y轴为layerStackRect的2倍,这种情况下会将layerStackRect拉伸显示:

sf-05-05.jpg

3.2、SurfaceComposerClient填充DisplayState

回到代码流程中,最终调用到SurfaceComposerClient中,将参数传递给DisplayState进行保存:

// frameworks/native/libs/gui/SurfaceComposerClient.cpp

void SurfaceComposerClient::Transaction::setDisplayProjection(const sp<IBinder>& token,
                                                              ui::Rotation orientation,
                                                              const Rect& layerStackRect,
                                                              const Rect& displayRect) {
    DisplayState& s(getDisplayState(token));
    s.orientation = orientation;
    s.layerStackSpaceRect = layerStackRect;
    s.orientedDisplaySpaceRect = displayRect;
    s.what |= DisplayState::eDisplayProjectionChanged;
}

DisplayState用于在Transaction中保存跟屏幕相关的参数,其定义如下:

// frameworks/native/include/gui/LayerState.h

struct DisplayState {
    enum {
        eSurfaceChanged = 0x01,           // VirtualDisplay Surface变化标记
        eLayerStackChanged = 0x02,        // Display Layerstack变化标记
        eDisplayProjectionChanged = 0x04, // Display 显示区域变化标记
        eDisplaySizeChanged = 0x08,       // Display size变化标记
        eFlagsChanged = 0x10,             // Display flags变化标记
    };

    DisplayState();
    void merge(const DisplayState& other);
    void sanitize(int32_t permissions);

    uint32_t what = 0;
    uint32_t flags = 0;
    sp<IBinder> token;                    // 物理屏token
    sp<IGraphicBufferProducer> surface;   // 虚拟屏surface

    ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;  // layerstack

    ui::Rotation orientation = ui::ROTATION_0;            // 屏幕方向
    Rect layerStackSpaceRect = Rect::EMPTY_RECT;          // 逻辑屏区域
    Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT;     // 物理屏实际内容显示区域

    uint32_t width = 0;      // 逻辑屏宽
    uint32_t height = 0;     // 逻辑屏高

    status_t write(Parcel& output) const;
    status_t read(const Parcel& input);
};

在Transaction开始apply时,会将所有DisplayState传递给surfaceflinger进行应用。

3.3、SurfaceFlinger::setDisplayStateLocked()更新DisplayDeviceState

进入surfaceflinger进程后,在收到VSYNC信号后执行commit()提交时,在setDisplayStateLocked()方法中,将SurfaceComposerClient中传入的DisplayState读取并将值更新给DisplayDeviceState对象:

// frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) {
    const ssize_t index = mCurrentState.displays.indexOfKey(s.token);
    if (index < 0) return 0;

    uint32_t flags = 0;
    // 获取DisplayDevice对应DisplayDeviceState
    DisplayDeviceState& state = mCurrentState.displays.editValueAt(index);

    const uint32_t what = s.what;
    // 更新surface
    if (what & DisplayState::eSurfaceChanged) {
        if (IInterface::asBinder(state.surface) != IInterface::asBinder(s.surface)) {
            state.surface = s.surface;
            flags |= eDisplayTransactionNeeded;
        }
    }
    // 更新LayerStack
    if (what & DisplayState::eLayerStackChanged) {
        if (state.layerStack != s.layerStack) {
            state.layerStack = s.layerStack;
            flags |= eDisplayTransactionNeeded;
        }
    }
    // 更新flag
    if (what & DisplayState::eFlagsChanged) {
        if (state.flags != s.flags) {
            state.flags = s.flags;
            flags |= eDisplayTransactionNeeded;
        }
    }
    // 更新orientation、layeStackRect、displayRect
    if (what & DisplayState::eDisplayProjectionChanged) {
        if (state.orientation != s.orientation) {
            state.orientation = s.orientation;
            flags |= eDisplayTransactionNeeded;
        }
        if (state.orientedDisplaySpaceRect != s.orientedDisplaySpaceRect) {
            state.orientedDisplaySpaceRect = s.orientedDisplaySpaceRect;
            flags |= eDisplayTransactionNeeded;
        }
        if (state.layerStackSpaceRect != s.layerStackSpaceRect) {
            state.layerStackSpaceRect = s.layerStackSpaceRect;
            flags |= eDisplayTransactionNeeded;
        }
    }
    // 更新宽、高
    if (what & DisplayState::eDisplaySizeChanged) {
        if (state.width != s.width) {
            state.width = s.width;
            flags |= eDisplayTransactionNeeded;
        }
        if (state.height != s.height) {
            state.height = s.height;
            flags |= eDisplayTransactionNeeded;
        }
    }

    return flags;
}

DisplayDeviceState表示具体的显示设备的状态集,其中定义了显示设备的宽、高、LayerStack等信息,DisplayDevice根据DisplayDeviceState中的配置进行创建或更新。

3.4、SurfaceFlinger::processDisplayChanged()处理DisplayDevice更新

完成DisplayDeviceState更新后,经过processDisplayChangesLocked()方法进入processDisplayChanged()方法中,根据mCurrentState.displaysmDrawingState.displays差异,开始向DisplayDevice中更新变更属性:

// frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
                                           const DisplayDeviceState& currentState,
                                           const DisplayDeviceState& drawingState) {
    const sp<IBinder> currentBinder = IInterface::asBinder(currentState.surface);
    const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface);

    // Recreate the DisplayDevice if the surface or sequence ID changed.
    ......

    if (const auto display = getDisplayDeviceLocked(displayToken)) {
        ......
        // 更新DisplayDevice的显示区域
        if ((currentState.orientation != drawingState.orientation) ||
            (currentState.layerStackSpaceRect != drawingState.layerStackSpaceRect) ||
            (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
            display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
                                   currentState.orientedDisplaySpaceRect);

            if (display->getId() == mActiveDisplayId) {
                mActiveDisplayTransformHint = display->getTransformHint();
                sActiveDisplayRotationFlags =
                        ui::Transform::toRotationFlags(display->getOrientation());
            }
        }
        // 更新DisplayDevice的宽高,默认仅用于虚拟屏屏幕大小的更改
        if (currentState.width != drawingState.width ||
            currentState.height != drawingState.height) {
                display->setDisplaySize(currentState.width, currentState.height);
            if (display->getId() == mActiveDisplayId) {
                onActiveDisplaySizeChanged(*display);
            }
        }
    }
}

SurfaceFlinger``::processDisplayChanged()方法用于处理Display发生变化后的操作,如新增Display、移除Display、Display属性变更等,这个方法中,会将DisplayDeviceState中的参数通过DisplayDevice::setProjection()方法传递给DisplayDevice:

// frameworks/native/services/surfaceflinger/DisplayDevice.cpp

void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect,
                                  Rect orientedDisplaySpaceRect) {
    mOrientation = orientation;

    ......
    // 给Composition::Display设置显示区域
    getCompositionDisplay()->setProjection(transformOrientation, layerStackSpaceRect,
                                           orientedDisplaySpaceRect);
}

以上方法中,又将参数直接传递给了compositionengine::Display对象,即该DisplayDevice对应的Output对象。

3.5、Output::setProjection()设置显示区域

Output::setProjection()中,最终将显示方向、LayerStack区域大小、实际内容显示区域大小更新给了OutputCompositionState中的对应属性:

// frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp

void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
                           const Rect& orientedDisplaySpaceRect) {
    auto& outputState = editState();

    // 设置物理显示空间displaySpace的方向
    outputState.displaySpace.setOrientation(orientation);

    // 设置定向显示空间orientedDisplaySpace的Bound和Content, 
    // 其大小跟displaySpace一致,但方向始终为0
    ui::Size orientedSize = outputState.displaySpace.getBounds();
    if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
        std::swap(orientedSize.width, orientedSize.height);
    }
    // 设置显示范围,同displaySpace范围,宽高随旋转方向而交换
    outputState.orientedDisplaySpace.setBounds(orientedSize);
    // 设置内容区域
    outputState.orientedDisplaySpace.setContent(orientedDisplaySpaceRect);

    // 根据orientation值获取显示方向flag
    const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation);
    ui::Transform rotation;
    // 根据orientation值创建一个变换对象,用于确定displaySpace.cotent区域
    if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
        const auto displaySize = outputState.displaySpace.getBoundsAsRect();
        rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height());
    }
    // 设置displaySpace的content
    outputState.displaySpace.setContent(rotation.transform(orientedDisplaySpaceRect));

    // 设置framebufferSpace的Content
    outputState.framebufferSpace.setOrientation(orientation);
    const auto scale = getScale(outputState.displaySpace.getBoundsAsRect(),
                                outputState.framebufferSpace.getBoundsAsRect());
    outputState.framebufferSpace.setContent(
            outputState.displaySpace.getContent().scale(scale.x, scale.y));

    // 设置layerStackSpace的Bound和Content
    outputState.layerStackSpace.setContent(layerStackSpaceRect);
    outputState.layerStackSpace.setBounds(
            ui::Size(layerStackSpaceRect.getWidth(), layerStackSpaceRect.getHeight()));

    outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace);
    outputState.needsFiltering = outputState.transform.needsBilinearFiltering();
    dirtyEntireOutput();
}

以上方法中,对4个ProjectionStack类型变量进行了更新。ProjectionSpace表示投影空间,只有三个属性:空间范围、内容区域、显示方向:

// frameworks/native/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h

class ProjectionSpace {
public:
    ......
private:
    // 该空间范围大小
    ui::Size mBounds = ui::Size();
    // 该空间显示内容区域
    Rect mContent = Rect();
    // 方向
    ui::Rotation mOrientation = ui::ROTATION_0;
};

OutputCompositionState是每个合成输出对象在合成过程中用于记录各种状态的对象,其中涉及到显示区域相关属性如下:

// frameworks/native/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
    
namespace compositionengine::impl {

struct OutputCompositionState {
    // 用来确定哪些Layer应当被包含在当前的输出中。
    // LayerFilter定义了一组规则,根据这些规则来过滤或选择特定的图层
    ui::LayerFilter layerFilter;

    // 表示当前LayerStack内的所有Layer共有的空间大小,也即逻辑屏的大小,方向始终是ROTATION_0
    ProjectionSpace layerStackSpace;

    // 定向显示空间,大小和displaySpace相同,但方向始终是ROTATION_0
    ProjectionSpace orientedDisplaySpace;

    // framebuffer空间,在不开启缩放模式时,其大小跟displaySpace相同
    ProjectionSpace framebufferSpace;

    // 物理显示空间,其大小跟当前物理屏幕大小一致,其中内容来自orientedDisplaySpace,可以进行旋转
    ProjectionSpace displaySpace;

    // 用于layerStackSpace到displaySpace的变换对象,如旋转、缩放等
    ui::Transform transform;

    // If true, RenderEngine filtering should be enabled
    bool needsFiltering{false};
};
} // namespace compositionengine::impl

下面逐一来看这些设置。

3.5.1、设置orientedDisplaySpace

orientedDisplaySpace表示定向显示空间。

  • 方向始终为ROTATE_0
  • 显示范围同displaySpace,为实际屏幕范围大小,但宽高跟随方向而变;
  • 内容区域为传入的实际显示区域大小。
// frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp

    // 设置定向显示空间orientedDisplaySpace的Bound和Content, 
    // 其大小跟displaySpace一致,但方向始终为0,因此需要根据方向进行变换
    ui::Size orientedSize = outputState.displaySpace.getBounds();
    if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
        std::swap(orientedSize.width, orientedSize.height);
    }
    // 设置显示范围,同displaySpace范围,宽高随旋转方向而交换
    outputState.orientedDisplaySpace.setBounds(orientedSize);
    // 设置内容区域
    outputState.orientedDisplaySpace.setContent(orientedDisplaySpaceRect);

3.5.2、设置displaySpace

displaySpace表示物理屏幕显示空间,用来描述实际显示空间。

  • 方向可旋转;
  • 显示范围在创建时确认,始终保持不变:
// frameworks/native/services/surfaceflinger/CompositionEngine/src/Display.cpp

void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
    ......
    // 设置displaySpace的显示范围为屏幕宽高
    editState().displaySpace.setBounds(args.pixels);
    setName(args.name);
}
  • 内容区域由显示方向结合显示区域进行矩阵计算后确定。
// frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp

    // 设置物理显示空间displaySpace的方向
    outputState.displaySpace.setOrientation(orientation);
    
    // 根据orientation值获取显示方向flag
    const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation);
    ui::Transform rotation;
    // 根据orientation值创建一个变换对象,用于确定displaySpace.cotent区域
    if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
        const auto displaySize = outputState.displaySpace.getBoundsAsRect();
        rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height());
    }
    // 设置displaySpace的content
    outputState.displaySpace.setContent(rotation.transform(orientedDisplaySpaceRect));

3.5.3、设置framebufferSpace

framebufferSpace表示framebuffer的显示空间。

  • 方向和显示方向保持一致;
  • 显示范围跟framebuffer一致,也等于屏幕的宽高,在创建framebuffer时就会设置:
// frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp

void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
    mRenderSurface = std::move(surface);
    const auto size = mRenderSurface->getSize();
    editState().framebufferSpace.setBounds(size);

    dirtyEntireOutput();
}
  • 内容区域跟displaySpace一致(非缩放模式时);
// frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp

    // 设置framebufferSpace的Content
    outputState.framebufferSpace.setOrientation(orientation);
    const auto scale = getScale(outputState.displaySpace.getBoundsAsRect(),
                                outputState.framebufferSpace.getBoundsAsRect());
    outputState.framebufferSpace.setContent(
            outputState.displaySpace.getContent().scale(scale.x, scale.y));

3.5.4、设置layerStackSpace

表示当前LayerStack内的所有Layer共有的空间大小,也即逻辑屏的大小。

  • 方向始终为ROTATE_0
  • 显示范围为传入的layerStackSpaceRect参数宽高;
  • 内容区域为传入的layerStackSpaceRect区域。
// frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp

    // 设置layerStackSpace的Bound和Content
    outputState.layerStackSpace.setContent(layerStackSpaceRect);
    outputState.layerStackSpace.setBounds(
            ui::Size(layerStackSpaceRect.getWidth(), layerStackSpaceRect.getHeight()));

这里对OutputCompositionState的以上属性完成更新后,在合成过程中,会应用这些属性,更新显示区域,通过调整layerStackSpace、orientedDisplaySpace、framebufferSpace和displaySpace的方向、范围、大小、变换方式,就能实现不同的显示方式。

正常情况下,这几个空间大小一般都是相同的,如果出现显示区域异常的现象,那么可能就是其中某个区域更新异常了,比如以下三种场景:

sf-05-06.jpgsf-05-07.jpgsf-05-08.jpg

以上整个过程时序图如下:

3.6、dump信息查看显示区域

可以通过dump SurfaceFlinger查看屏幕显示区域相关状态:

$ adb shell dumpsys SurfaceFlinger
# 或者
$ adb shell dumpsys SurfaceFlinger | grep -Ei "layerStackSpace=" -A4

也可通过dump display查看逻辑屏显示区域和实际内容显示区域:

adb shell dumpsys display

四、LayerStack更新

LayerStack是用于管理在一个屏幕上所显示Layer的一个ID值,和逻辑屏ID一致,简单说就是一个逻辑屏上显示的所有Layer集合,一个Layer只能和一个LayerStack相关联,但LayerStack可以和多个Display关联。

surfaceflinger中利用LayerStack创建了一个对应的LayerFilter对象,用于筛选和匹配,从而实现区分一个Layer在不同屏幕上的显示情况。在创建Layer时,会为Layer指定一个LayerStack,当Layer的LayerStack和DisplayDevice的LayerStack一致时,说明该Layer可以显示在该DisplayDevice上。反之,将无法显示。

当系统灭屏后,LayerStack值将被设置为-1,这样就匹配不上所有的Layer,surfaceflinger中就不会进行合成输出了。

surfaceflinger中LayerStack定义如下:

// frameworks/native/libs/ui/include/ui/LayerStack.h

namespace android::ui {

// LayerStack结构体
struct LayerStack {
    // 实际为一个uint32_t整型
    uint32_t id = UINT32_MAX;

    template <typename T>
    static constexpr LayerStack fromValue(T v) {
        if (ftl::cast_safety<uint32_t>(v) == ftl::CastSafety::kSafe) {
            return {static_cast<uint32_t>(v)};
        }

        return {};
    }
};
}

下面看下具体更新过程。

LayerStack的更新过程和显示区域更新过程基本一致,都是从DisplayManagerService中向surfaceflinger进行更新:

// frameworks/base/services/core/java/com/android/server/display/LogicalDisplay.java

public void configureDisplayLocked(SurfaceControl.Transaction t,
        DisplayDevice device,
        boolean isBlanked) {
        
        ......
    // 设置LayerStack
    device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack, mDisplayId);
    // 设置Display Flag进行特殊行为标记
    device.setDisplayFlagsLocked(t,
            (isEnabledLocked() && device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE)
                    ? SurfaceControl.DISPLAY_RECEIVES_INPUT
    : 0);
    // 设置物理-逻辑屏幕映射
    device.setProjectionLocked(t, orientation, mTempLayerStackRect, mTempDisplayRect);
}

以上方法中:

  • isBlanked表示Display的power state是否为OFF;

  • 若Display state为OFF时,会将对应屏幕LayerStack设置为-1,进而surfaceflinger中不再合成和输出Layer;

  • 当Display state为非OFF状态时,会将对应逻辑屏ID作为LayerStack设置。

DMS中设置完成,传递给SurfaceComposerClient中后,会将layerStack也保存在DisplayState.layerStack中:

// frameworks/native/libs/gui/SurfaceComposerClient.cpp

void SurfaceComposerClient::Transaction::setDisplayLayerStack(const sp<IBinder>& token,
                                                              ui::LayerStack layerStack) {
    DisplayState& s(getDisplayState(token));
    s.layerStack = layerStack;
    s.what |= DisplayState::eLayerStackChanged;
}

在Transaction发起apply后,传递给surfaceflinger进行应用。

后续的流程跟设置显示区域基本一致,最终在SurfaceFlinger::setDisplayStateLocked()中将LayerStack更新给了DisplayDeviceState中,并在SurfaceFlinger::processDisplayChanged()方法中更新给DisplayDevice和Output:

// frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
                                           const DisplayDeviceState& currentState,
                                           const DisplayDeviceState& drawingState) {
    ......
    if (const auto display = getDisplayDeviceLocked(displayToken)) {
        // 根据LayerStack创建并更新LayerFilter
        if (currentState.layerStack != drawingState.layerStack) {
            display->setLayerFilter(
                    makeLayerFilterForDisplay(display->getId(), currentState.layerStack));
        }
        ......
    }
    ......
}

以上方法中,会通过makeLayerFilterForDisplay()方法创建一个LayerFilter对象,并将LayerFilter设置给DisplayDevice。

4.1、创建LayerFilter

LayerFilter可以理解为具体一个合成输出目标上的Layer过滤器,surfaceflinger中会为Layer和Output分别创建各自的LayerFilter,只有当Layer的LayerFilter和Output的LayerFilter匹配时,该Layer才会在该Output上进行合成和显示。LayerFilter定义如下:

// frameworks/native/libs/ui/include/ui/LayerStack.h
// A LayerFilter determines if a layer is included for output to a display.
struct LayerFilter {
    // LayerStack ID
    LayerStack layerStack;

    // 为true时,表示仅对内置屏合成输出
    bool toInternalDisplay = false;

    // 返回true时表示匹配成功
    bool includes(LayerFilter other) const {
        // layerstack id不一致时,返回false,对应Layer将不会参与到合成输出
        if (other.layerStack == INVALID_LAYER_STACK || other.layerStack != layerStack) {
            return false;
        }
        // 当Layer的LayerFilter::toInternalDisplay为true时,
        // Output的LayerFilter::toInternalDisplay为true时才会参与到合成输出
        return !other.toInternalDisplay || toInternalDisplay;
    }
};
  • layerStack:表示LayerStack值,即逻辑屏ID值;

  • toInternalDisplay:

    • 对于Layer来说,表示该Layer是否仅向内置屏幕进行合成输出;
    • 对于Output来说,表示该Output是否是内置屏对应的Output;

4.1.1、为Output创建LayerFilter

为Output创建LayerFilter的makeLayerFilterForDisplay()方法如下:

// frameworks/native/services/surfaceflinger/SurfaceFlinger.h

    ui::LayerFilter makeLayerFilterForDisplay(DisplayId displayId, ui::LayerStack layerStack)
            REQUIRES(mStateLock) {
        return {layerStack,
                PhysicalDisplayId::tryCast(displayId)
                        .and_then(display::getPhysicalDisplay(mPhysicalDisplays))
                        .transform(&display::PhysicalDisplay::isInternal)
                        .value_or(false)};
    }

完成LayerFilter创建后,将LayterFilter设置给Output:

// frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp

void Output::setLayerFilter(ui::LayerFilter filter) {
    editState().layerFilter = filter;
    dirtyEntireOutput();
}

4.1.2、为Layer创建LayerFilter

通过SurfaceControl.Transaction中,可以为Layer指定一个LayerStack:

// frameworks/base/core/java/android/view/SurfaceControl.java

public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
    checkPreconditions(sc);
    nativeSetLayerStack(mNativeObject, sc.mNativeObject, layerStack);
    return this;
}

默认LayerStack为0,即为默认主LogicalDisplay上显示。

在提交和合成过程中,会根据Layer的layerStack和其他属性,为Layer创建一个LayerFilter,用于判断是否针对指定屏幕进行输出:

// frameworks/native/services/surfaceflinger/Layer.h
ui::LayerFilter getOutputFilter() const {
    return {getLayerStack(), isInternalDisplayOverlay()};
}

// frameworks/native/services/surfaceflinger/Layer.cpp
bool Layer::isInternalDisplayOverlay() const {
    const State& s(mDrawingState);
    // 如果带有eLayerSkipScreenshot标记,则表示仅仅向内置屏幕输出
    if (s.flags & layer_state_t::eLayerSkipScreenshot) {
        return true;
    }

    sp<Layer> parent = mDrawingParent.promote();
    return parent && parent->isInternalDisplayOverlay();
}

以上方法中,如果带有eLayerSkipScreenshot标记,则toInternalDisplay为true。

4.2、LayerFilter的匹配

在合成过程Output::ensureOutputLayerIfVisible()方法中,将会对Layer的layerFilter和Output的LayerFilter进行匹配,以决定该Layer是否要合成输出到该Output上:

// frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp

void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE,
                                        compositionengine::Output::CoverageState& coverage) {
    ......
    // Only consider the layers on this output
    if (!includesLayer(layerFE)) {
        return;
    }
    ......
}

// frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp
bool Output::includesLayer(ui::LayerFilter filter) const {
    return getState().layerFilter.includes(filter);
}

最后,正是在LayerFilter::includes()方法中进行匹配,并返回结果,当返回结果为false时,说明Layer和Output不匹配。

这也是eLayerSkipScreenshot标记所表示含义的实现位置,当携带有eLayerSkipScreenshot的Layer,只会显示在内置屏幕上,在截图、录屏中都不可见。

通过LayerStack和LayerFilter,就可以实现Layer的在指定屏幕上的显示。