Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 08dcdbe9 authored by Chavi Weingarten's avatar Chavi Weingarten Committed by Automerger Merge Worker
Browse files

Merge "Allow offscreen mirrored layers to be captured." into sc-v2-dev am: ebff6bd2

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/16125500

Change-Id: I202573a959fbb3f92dd72bf1b32218012499f020
parents 8ab6e7de ebff6bd2
Loading
Loading
Loading
Loading
+14 −0
Original line number Original line Diff line number Diff line
@@ -94,8 +94,22 @@ void LayerRenderArea::render(std::function<void()> drawLayers) {
    // no need to check rotation because there is none
    // no need to check rotation because there is none
    mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight();
    mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight();


    // If layer is offscreen, update mirroring info if it exists
    if (mLayer->isRemovedFromCurrentState()) {
        mLayer->traverse(LayerVector::StateSet::Drawing,
                         [&](Layer* layer) { layer->updateMirrorInfo(); });
        mLayer->traverse(LayerVector::StateSet::Drawing,
                         [&](Layer* layer) { layer->updateCloneBufferInfo(); });
    }

    if (!mChildrenOnly) {
    if (!mChildrenOnly) {
        mTransform = mLayer->getTransform().inverse();
        mTransform = mLayer->getTransform().inverse();
        // If the layer is offscreen, compute bounds since we don't compute bounds for offscreen
        // layers in a regular cycles.
        if (mLayer->isRemovedFromCurrentState()) {
            FloatRect maxBounds = mFlinger.getMaxDisplayBounds();
            mLayer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
        }
        drawLayers();
        drawLayers();
    } else {
    } else {
        uint32_t w = static_cast<uint32_t>(getWidth());
        uint32_t w = static_cast<uint32_t>(getWidth());
+20 −20
Original line number Original line Diff line number Diff line
@@ -2455,7 +2455,7 @@ void SurfaceFlinger::postComposition() {
    }
    }
}
}


void SurfaceFlinger::computeLayerBounds() {
FloatRect SurfaceFlinger::getMaxDisplayBounds() {
    // Find the largest width and height among all the displays.
    // Find the largest width and height among all the displays.
    int32_t maxDisplayWidth = 0;
    int32_t maxDisplayWidth = 0;
    int32_t maxDisplayHeight = 0;
    int32_t maxDisplayHeight = 0;
@@ -2473,8 +2473,13 @@ void SurfaceFlinger::computeLayerBounds() {


    // Ignore display bounds for now since they will be computed later. Use a large Rect bound
    // Ignore display bounds for now since they will be computed later. Use a large Rect bound
    // to ensure it's bigger than an actual display will be.
    // to ensure it's bigger than an actual display will be.
    FloatRect maxBounds(-maxDisplayWidth * 10, -maxDisplayHeight * 10, maxDisplayWidth * 10,
    FloatRect maxBounds = FloatRect(-maxDisplayWidth * 10, -maxDisplayHeight * 10,
                        maxDisplayHeight * 10);
                                    maxDisplayWidth * 10, maxDisplayHeight * 10);
    return maxBounds;
}

void SurfaceFlinger::computeLayerBounds() {
    FloatRect maxBounds = getMaxDisplayBounds();
    for (const auto& layer : mDrawingState.layersSortedByZ) {
    for (const auto& layer : mDrawingState.layersSortedByZ) {
        layer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
        layer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
    }
    }
@@ -6167,9 +6172,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
    sp<Layer> parent;
    sp<Layer> parent;
    Rect crop(args.sourceCrop);
    Rect crop(args.sourceCrop);
    std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
    std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
    Rect layerStackSpaceRect;
    ui::Dataspace dataspace;
    ui::Dataspace dataspace;
    bool captureSecureLayers;


    // Call this before holding mStateLock to avoid any deadlocking.
    // Call this before holding mStateLock to avoid any deadlocking.
    bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
    bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
@@ -6178,7 +6181,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
        Mutex::Autolock lock(mStateLock);
        Mutex::Autolock lock(mStateLock);


        parent = fromHandle(args.layerHandle).promote();
        parent = fromHandle(args.layerHandle).promote();
        if (parent == nullptr || parent->isRemovedFromCurrentState()) {
        if (parent == nullptr) {
            ALOGE("captureLayers called with an invalid or removed parent");
            ALOGE("captureLayers called with an invalid or removed parent");
            return NAME_NOT_FOUND;
            return NAME_NOT_FOUND;
        }
        }
@@ -6217,39 +6220,36 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
            }
            }
        }
        }


        const auto display = findDisplay(WithLayerStack(parent->getLayerStack()));
        if (!display) {
            return NAME_NOT_FOUND;
        }

        layerStackSpaceRect = display->getLayerStackSpaceRect();

        // The dataspace is depended on the color mode of display, that could use non-native mode
        // The dataspace is depended on the color mode of display, that could use non-native mode
        // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
        // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
        // and failed if display is not in native mode. This provide a way to force using native
        // and failed if display is not in native mode. This provide a way to force using native
        // colors when capture.
        // colors when capture.
        dataspace = args.dataspace;
        dataspace = args.dataspace;
        if (dataspace == ui::Dataspace::UNKNOWN) {
        if (dataspace == ui::Dataspace::UNKNOWN) {
            auto display = findDisplay(WithLayerStack(parent->getLayerStack()));
            if (!display) {
                // If the layer is not on a display, use the dataspace for the default display.
                display = getDefaultDisplayDeviceLocked();
            }

            const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
            const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
            dataspace = pickDataspaceFromColorMode(colorMode);
            dataspace = pickDataspaceFromColorMode(colorMode);
        }
        }


        captureSecureLayers = args.captureSecureLayers && display->isSecure();
    } // mStateLock
    } // mStateLock


    // really small crop or frameScale
    // really small crop or frameScale
    if (reqSize.width <= 0) {
    if (reqSize.width <= 0 || reqSize.height <= 0) {
        reqSize.width = 1;
        ALOGW("Failed to captureLayes: crop or scale too small");
    }
        return BAD_VALUE;
    if (reqSize.height <= 0) {
        reqSize.height = 1;
    }
    }


    Rect layerStackSpaceRect(0, 0, reqSize.width, reqSize.height);
    bool childrenOnly = args.childrenOnly;
    bool childrenOnly = args.childrenOnly;
    RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
    RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
        return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
        return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
                                                 childrenOnly, layerStackSpaceRect,
                                                 childrenOnly, layerStackSpaceRect,
                                                 captureSecureLayers);
                                                 args.captureSecureLayers);
    });
    });


    auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
    auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
+1 −0
Original line number Original line Diff line number Diff line
@@ -337,6 +337,7 @@ public:
    // Disables expensive rendering for all displays
    // Disables expensive rendering for all displays
    // This is scheduled on the main thread
    // This is scheduled on the main thread
    void disableExpensiveRendering();
    void disableExpensiveRendering();
    FloatRect getMaxDisplayBounds();


protected:
protected:
    // We're reference counted, never destroy SurfaceFlinger directly
    // We're reference counted, never destroy SurfaceFlinger directly
+0 −11
Original line number Original line Diff line number Diff line
@@ -52,17 +52,6 @@ protected:
    }
    }
};
};


TEST_F(InvalidHandleTest, createSurfaceInvalidParentHandle) {
    // The createSurface is scheduled now, we could still get a created surface from createSurface.
    // Should verify if it actually added into current state by checking the screenshot.
    auto notSc = mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0,
                                     mNotSc->getHandle());
    LayerCaptureArgs args;
    args.layerHandle = notSc->getHandle();
    ScreenCaptureResults captureResults;
    ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
}

TEST_F(InvalidHandleTest, captureLayersInvalidHandle) {
TEST_F(InvalidHandleTest, captureLayersInvalidHandle) {
    LayerCaptureArgs args;
    LayerCaptureArgs args;
    args.layerHandle = mNotSc->getHandle();
    args.layerHandle = mNotSc->getHandle();
+55 −0
Original line number Original line Diff line number Diff line
@@ -273,6 +273,61 @@ TEST_F(MirrorLayerTest, InitialMirrorState) {
    }
    }
}
}


// Test that a mirror layer can be screenshot when offscreen
TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) {
    const auto display = SurfaceComposerClient::getInternalDisplayToken();
    ui::DisplayMode mode;
    SurfaceComposerClient::getActiveDisplayMode(display, &mode);
    const ui::Size& size = mode.resolution;

    sp<SurfaceControl> grandchild =
            createLayer("Grandchild layer", 50, 50, ISurfaceComposerClient::eFXSurfaceBufferState,
                        mChildLayer.get());
    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(grandchild, Color::BLUE, 50, 50));
    Rect childBounds = Rect(50, 50, 450, 450);

    asTransaction([&](Transaction& t) {
        t.setCrop(grandchild, Rect(0, 0, 50, 50)).show(grandchild);
        t.setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
    });

    sp<SurfaceControl> mirrorLayer = nullptr;
    {
        // Run as system to get the ACCESS_SURFACE_FLINGER permission when mirroring
        UIDFaker f(AID_SYSTEM);
        // Mirror mChildLayer
        mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
        ASSERT_NE(mirrorLayer, nullptr);
    }

    // Show the mirror layer, but don't reparent to a layer on screen.
    Transaction().show(mirrorLayer).apply();

    {
        SCOPED_TRACE("Offscreen Mirror");
        auto shot = screenshot();
        shot->expectColor(Rect(0, 0, size.getWidth(), 50), Color::RED);
        shot->expectColor(Rect(0, 0, 50, size.getHeight()), Color::RED);
        shot->expectColor(Rect(450, 0, size.getWidth(), size.getHeight()), Color::RED);
        shot->expectColor(Rect(0, 450, size.getWidth(), size.getHeight()), Color::RED);
        shot->expectColor(Rect(100, 100, 450, 450), Color::GREEN);
        shot->expectColor(Rect(50, 50, 100, 100), Color::BLUE);
    }

    {
        SCOPED_TRACE("Capture Mirror");
        // Capture just the mirror layer and child.
        LayerCaptureArgs captureArgs;
        captureArgs.layerHandle = mirrorLayer->getHandle();
        captureArgs.sourceCrop = childBounds;
        std::unique_ptr<ScreenCapture> shot;
        ScreenCapture::captureLayers(&shot, captureArgs);
        shot->expectSize(childBounds.width(), childBounds.height());
        shot->expectColor(Rect(0, 0, 50, 50), Color::BLUE);
        shot->expectColor(Rect(50, 50, 400, 400), Color::GREEN);
    }
}

} // namespace android
} // namespace android


// TODO(b/129481165): remove the #pragma below and fix conversion issues
// TODO(b/129481165): remove the #pragma below and fix conversion issues
Loading