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

Commit ee5b1fdb authored by Cairn Overturf's avatar Cairn Overturf
Browse files

Fix shadows when device is rotated

- Made setGlobalShadowSettings synchronous since oneway and synchronous calls are not guaranteed to be ordered.  This is helpful for testing and this function is only called once on startup.
- Fixed cropped shadow test by moving shadow casting layer to a position that is the same relative to the light pos, independent of device resolution.

Bug: 377194534
Flag: EXEMPT bugfix
Test: Open freeform windows and rotate your device and observe that shadows are cast in a consistent direction. Before the change, T6 shadows were always incorrectly cast towards the charging port.
Change-Id: I72a7586f40ae35a0ac449ad6b920976de145f446
parent 548c2a1b
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -503,7 +503,7 @@ interface ISurfaceComposer {
     * lightRadius
     * lightRadius
     *      Radius of the light casting the shadow.
     *      Radius of the light casting the shadow.
     */
     */
    oneway void setGlobalShadowSettings(in Color ambientColor, in Color spotColor, float lightPosY, float lightPosZ, float lightRadius);
    void setGlobalShadowSettings(in Color ambientColor, in Color spotColor, float lightPosY, float lightPosZ, float lightRadius);


    /**
    /**
     * Gets whether a display supports DISPLAY_DECORATION layers.
     * Gets whether a display supports DISPLAY_DECORATION layers.
+10 −3
Original line number Original line Diff line number Diff line
@@ -238,6 +238,7 @@ static inline SkM44 getSkM44(const android::mat4& matrix) {
                 matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
                 matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]);
}
}


[[maybe_unused]]
static inline SkPoint3 getSkPoint3(const android::vec3& vector) {
static inline SkPoint3 getSkPoint3(const android::vec3& vector) {
    return SkPoint3::Make(vector.x, vector.y, vector.z);
    return SkPoint3::Make(vector.x, vector.y, vector.z);
}
}
@@ -1377,10 +1378,16 @@ void SkiaRenderEngine::drawShadow(SkCanvas* canvas,
    const auto flags =
    const auto flags =
            settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;
            settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag;


    // DrawShadow expects the light pos in device space.
    // Shadow settings is in layer space (which is our current canvas transform).
    SkMatrix deviceFromLayer = canvas->getTotalMatrix();
    SkPoint lightPos = {settings.lightPos.x, settings.lightPos.y}; // lightPos is in layer space
    deviceFromLayer.mapPoints(&lightPos, 1);                       // lightPos is in device space

    SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ),
    SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ),
                              getSkPoint3(settings.lightPos), settings.lightRadius,
                              SkPoint3{lightPos.fX, lightPos.fY, settings.lightPos.z},
                              getSkColor(settings.ambientColor), getSkColor(settings.spotColor),
                              settings.lightRadius, getSkColor(settings.ambientColor),
                              flags);
                              getSkColor(settings.spotColor), flags);
}
}


void SkiaRenderEngine::onActiveDisplaySizeChanged(ui::Size size) {
void SkiaRenderEngine::onActiveDisplaySizeChanged(ui::Size size) {
+13 −5
Original line number Original line Diff line number Diff line
@@ -348,11 +348,19 @@ void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
        return;
        return;
    }
    }


    // Shift the spot light x-position to the middle of the display and then
    // The light source should be at (screenWidth/2, globalShadowSettings.lightPos.y) in
    // offset it by casting layer's screen pos.
    // screenspace.
    state.lightPos.x =
    vec2 lightPosScreenSpace = {
            (static_cast<float>(layerStackRect.width()) / 2.f) - mSnapshot->transformedBounds.left;
            (static_cast<float>(layerStackRect.width()) / 2.f),
    state.lightPos.y -= mSnapshot->transformedBounds.top;
            state.lightPos.y,
    };

    // Skia expects light pos in layer space.
    vec2 lightPosLayerSpace = mSnapshot->geomInverseLayerTransform.transform(lightPosScreenSpace);

    state.lightPos.x = lightPosLayerSpace.x;
    state.lightPos.y = lightPosLayerSpace.y;

    caster.shadow = state;
    caster.shadow = state;
}
}


+3 −0
Original line number Original line Diff line number Diff line
@@ -8301,6 +8301,9 @@ status_t SurfaceFlinger::setGlobalShadowSettings(const half4& ambientColor, cons
    // these values are overridden when calculating the shadow settings for a layer.
    // these values are overridden when calculating the shadow settings for a layer.
    mCurrentState.globalShadowSettings.lightPos.x = 0.f;
    mCurrentState.globalShadowSettings.lightPos.x = 0.f;
    mCurrentState.globalShadowSettings.length = 0.f;
    mCurrentState.globalShadowSettings.length = 0.f;

    setTransactionFlags(eTransactionNeeded);

    return NO_ERROR;
    return NO_ERROR;
}
}


+76 −6
Original line number Original line Diff line number Diff line
@@ -876,18 +876,24 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, CropElevationShadowByParent) {
    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize));
    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize));
    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::WHITE, parentSize, parentSize));
    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::WHITE, parentSize, parentSize));
    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::BLUE, size, size));


    // TODO(b/377194534): This test fails on Tangor because global shadow
    // settings seem to not respect rotation.
    SurfaceComposerClient::getDefault()->setGlobalShadowSettings({1, 0, 0, 0.5f}, {0, 1, 0, 0.5f},
    SurfaceComposerClient::getDefault()->setGlobalShadowSettings({1, 0, 0, 0.5f}, {0, 1, 0, 0.5f},
                                                                 0, 500.0f, 800.0f);
                                                                 0, 250.0f, 800.0f);

    // Global shadow settings are committed after snapshot updates so we have to perform a commit
    // before enabling layer shadows.
    Transaction().apply(true);


    std::this_thread::sleep_for(std::chrono::seconds(1));
    ui::Size resolution = mRenderPathHarness.getRotatedResolution();
    int x = resolution.width / 2;
    int y = 500;


    Transaction()
    Transaction()
            .setPosition(parent, x, y)
            .setCrop(parent, Rect(0, 0, parentSize / 2, parentSize))
            .setCrop(parent, Rect(0, 0, parentSize / 2, parentSize))
            .reparent(child, parent)
            .reparent(child, parent)
            .setPosition(child, size, size)
            .setCrop(child, Rect(0, 0, size, size))
            .setCrop(child, Rect(0, 0, size, size))
            .setPosition(child, size, size)
            .setPosition(child, size, size)
            .setCornerRadius(child, 20.0f)
            .setCornerRadius(child, 20.0f)
@@ -896,7 +902,7 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, CropElevationShadowByParent) {


    auto shot = getScreenCapture();
    auto shot = getScreenCapture();


    shot->expectBufferMatchesImageFromFile(Rect(0, 0, parentSize, parentSize),
    shot->expectBufferMatchesImageFromFile(Rect(x, y, x + parentSize, y + parentSize),
                                           "testdata/CropElevationShadowByParent.png");
                                           "testdata/CropElevationShadowByParent.png");
}
}


@@ -1146,6 +1152,70 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBufferFormat) {
        shot->expectColor(crop, Color::RED);
        shot->expectColor(crop, Color::RED);
    }
    }
}
}

class RotatedVirtualDisplayTest
      : public LayerTypeTransactionHarness,
        public ::testing::WithParamInterface<std::tuple<uint32_t, RenderPath, ui::Rotation>> {
public:
    RotatedVirtualDisplayTest()
          : LayerTypeTransactionHarness(std::get<0>(GetParam())),
            mRenderPathHarness(this, std::get<1>(GetParam()), std::get<2>(GetParam())) {}

    std::unique_ptr<ScreenCapture> getScreenCapture() {
        return mRenderPathHarness.getScreenCapture();
    }

protected:
    LayerRenderPathTestHarness mRenderPathHarness;
};

INSTANTIATE_TEST_CASE_P(RotatedVirtualDisplayTests, RotatedVirtualDisplayTest,
                        ::testing::Combine(::testing::Values(static_cast<uint32_t>(
                                                   ISurfaceComposerClient::eFXSurfaceBufferState)),
                                           ::testing::Values(RenderPath::VIRTUAL_DISPLAY),
                                           ::testing::Values(ui::Rotation::Rotation0,
                                                             ui::Rotation::Rotation90,
                                                             ui::Rotation::Rotation180,
                                                             ui::Rotation::Rotation270)));

TEST_P(RotatedVirtualDisplayTest, ElevationShadowHasCorrectOrientation) {
    sp<SurfaceControl> parent;
    sp<SurfaceControl> child;
    const uint32_t size = 64;
    const uint32_t parentSize = size * 3;
    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", parentSize, parentSize));
    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::WHITE, parentSize, parentSize));
    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));

    SurfaceComposerClient::getDefault()->setGlobalShadowSettings({1, 0, 0, 1.0f}, {0, 1, 0, 1.0f},
                                                                 0, 500.0f, 800.0f);

    // Global shadow settings are committed after snapshot updates so we have to perform a commit
    // before enabling layer shadows.
    Transaction().apply(true);

    ui::Size resolution = mRenderPathHarness.getRotatedResolution();
    int x = resolution.width / 2;
    int y = 500;
    Transaction()
            .setPosition(parent, x, y)
            .setCrop(parent, Rect(0, 0, parentSize, parentSize))
            .reparent(child, parent)
            .setCrop(child, Rect(0, 0, size, size))
            .setPosition(child, size, size)
            .setCornerRadius(child, 20.0f)
            .setShadowRadius(child, 20.0f)
            .apply(true);

    auto shot = getScreenCapture();

    shot->expectBufferMatchesImageFromFile(mRenderPathHarness.rotateRect(
                                                   Rect(x, y, x + parentSize, y + parentSize)),
                                           "testdata/Shadow_" +
                                                   mRenderPathHarness.getRotationName() + ".png");
}

} // 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