2015年2月24日 星期二

Android Layer && LayerStack

In SurfaceFlinger, the basic logic unit to show is a Layer (which corresponds to one ViewRootImpl in app process). The contents users see is a set of Layers with depth information. With depth info, some Layer displayed in front, while some in back, and this depth info is the z value. The larger, the nearer to user. WindowManagerService can adjust the z value through SurfaceControl.setLayer, which conveys this info to SurfaceFlinger so create different effect. For example, user can enable StrictMode violation flash in developer option, once there is violation, WMS creates a Layer, and make it visible for a while, then make it invisible. To prevent other Layer from hiding the flash effect, the z value of StrictMode Layer is adjusted so that it would be displayed on top to get attention.

The above works perfect for Layers showing on single display. When multiple displays are supported, we need a way to know which Layer to show on which display, hence the LayerStack. LayerStack is an attribute for both a Layer object and a display. When a Layer has same LayerStack as a display, it would be showed on that display. Effectively, LayerStack creates an depth space for a display, so that each display has its own depth space. One immediate use case for LayerStack is mirror mode. When a user plugin a HDMI to device, both displays shows the same content (mirror mode). WMS just set the LayerStack for HDMI display to the same value as panel, so that when SF composes contents for HDMI, it picks up same set of Layers as primary display.

Display LayerStack
In framework, a display device is described by a subclass of DisplayDevice. There are several different subclass of it, let's make it simple, using LocalDisplayDevice as an example. A LocalDisplayDevice means a physical device that can connect to the tablet or phone. Internal panel and HDMI are examples of LocalDisplayDevice. When a display is connected, framework creates an object of LocalDisplayDevice, and enumerates a LayerStack for this device (now LayerStack is same as display id, but they do not need to be the same), then using this LayerStack, it creates an associated LogicalDisplay object. One LogicalDisplay associates with one LocalDisplayDevice, and LogicalDisplay decides the LayerStack to use, then tell SurfaceFlinger through LocalDisplayDevice, LocalDisplayDevice probably can be think of an interface to SF for display related setting. So, basically if you connect HDMI on your device, you'll have 2 LogicalDisplay, each with its own LayerStack. But in mirror mode, LayerStack of both display is the same, what happened ? The trick is in DisplayManagerService.configureDisplayInTransactionLocked()

    private void configureDisplayInTransactionLocked(DisplayDevice device) {
        final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
        final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;

        // Find the logical display that the display device is showing.
        // Certain displays only ever show their own content.
        LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
        if (!ownContent) {
            if (display != null && !display.hasContentLocked()) {
                // If the display does not have any content of its own, then
                // automatically mirror the default logical display contents.
                display = null;
            }
            if (display == null) {
                display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
            }
        

        }
        ....
        display.configureDisplayInTransactionLocked(device, info.state == Display.STATE_OFF);        ....

    }

When mirror mode is in use, FLAG_OWN_CONTENT_ONLY is not set for LogicalDisplay of HDMI, so the LogicalDisplay used to configure HDMI monitor is that of internal panel (see above code), that's why in that case, the LayerStack in both display in SF are the same, hence showing the same contents.

LayerStack@Layer
Layer is the concept used in SurfaceFlinger. It has different face in different places. A Layer in SF has a corresponding WindowState in WMS, and a corresponding ViewRootImpl object in app process. When a ViewRootImpl object is created, it has an associated display, and this display decides the LayerStack value to be assigned by WMS and used in SF. In WMS, each WindowState object has its own WindowStateAnimator object, which ask SF to create a Layer object for it. Below code is from WindowStateAnimator.

    SurfaceControl createSurfaceLocked() {
        ....
        // Start a new transaction and apply position & offset.
        SurfaceControl.openTransaction();
        try {

            ....
            try {

                ....
                final DisplayContent displayContent = w.getDisplayContent();
                if (displayContent != null) {           

                mSurfaceControl.setLayerStack(displayContent.getDisplay().getLayerStack());
            }

            ....
        } finally {
            SurfaceControl.closeTransaction();
        }

       }

WMS maintains a DisplayContent object to store the list of WindowState object to show in this display. From DisplayContent, it can get the display object, then get the LayerStack of it, and assign this to the Layer object in SF.

沒有留言:

張貼留言