diff options
| author | 2021-09-22 06:11:43 -0700 | |
|---|---|---|
| committer | 2022-01-28 09:20:51 -0800 | |
| commit | c9589c17c0394b8ae9c738a8bdb3daa057ed4f9c (patch) | |
| tree | ea84bbbfdea1ad63b7e15e70f57454081d3e40a9 | |
| parent | 8c2859824db8f602a72ceff458f4374e6faf0a2a (diff) | |
SF: Clean up input info calculations
This CL simplifies the calculation of the input transform and frame for
Layers.
It also switches the calculations to use floats rather than intergers
for greater precesion when scaling.
Bug: 200787840
Bug: 179274888
Test: atest libgui_test
Change-Id: Ia1ed53b669caa867e5b98e72d5d71ed615222f8f
| -rw-r--r-- | libs/gui/tests/EndToEndNativeInputTest.cpp | 7 | ||||
| -rw-r--r-- | services/surfaceflinger/Layer.cpp | 137 | ||||
| -rw-r--r-- | services/surfaceflinger/Layer.h | 2 |
3 files changed, 64 insertions, 82 deletions
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 76844d2d83..06a0acae49 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -546,7 +546,10 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { } TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); + bgSurface->showAt(100, 100); + // In case we pass the very big inset without any checking. fgSurface->mInputInfo.surfaceInset = INT32_MAX; fgSurface->showAt(100, 100); @@ -554,8 +557,8 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); // expect no crash for overflow, and inset size to be clamped to surface size - injectTap(202, 202); - fgSurface->expectTap(1, 1); + injectTap(112, 124); + bgSurface->expectTap(12, 24); } // Ensure we ignore transparent region when getting screen bounds when positioning input frame. diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 8c77a8b648..fc73181c87 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2166,9 +2166,9 @@ Rect Layer::getInputBounds() const { return getCroppedBufferSize(getDrawingState()); } -void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTransform) { - Rect layerBounds = getInputBounds(); - if (!layerBounds.isValid()) { +void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) { + Rect tmpBounds = getInputBounds(); + if (!tmpBounds.isValid()) { info.flags = WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::NOT_FOCUSABLE; info.focusable = false; info.touchableRegion.clear(); @@ -2176,82 +2176,61 @@ void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTra // replaceTouchableRegionWithCrop. For that case, the input transform needs to be calculated // correctly to determine the coordinate space for input events. Use an empty rect so that // the layer will receive input in its own layer space. - layerBounds = Rect::EMPTY_RECT; - } - - const ui::Transform layerTransform = getInputTransform(); - // Transform that takes window coordinates to non-rotated display coordinates - ui::Transform t = displayTransform * layerTransform; - int32_t xSurfaceInset = info.surfaceInset; - int32_t ySurfaceInset = info.surfaceInset; - // Bring screenBounds into non-unrotated space - Rect screenBounds = displayTransform.transform(Rect{mScreenBounds}); - - const float xScale = t.getScaleX(); - const float yScale = t.getScaleY(); - if (xScale != 1.0f || yScale != 1.0f) { - xSurfaceInset = std::round(xSurfaceInset * xScale); - ySurfaceInset = std::round(ySurfaceInset * yScale); - } - - // Transform the layer bounds from layer coordinate space to display coordinate space. - Rect transformedLayerBounds = t.transform(layerBounds); - - // clamp inset to layer bounds - xSurfaceInset = (xSurfaceInset >= 0) - ? std::min(xSurfaceInset, transformedLayerBounds.getWidth() / 2) - : 0; - ySurfaceInset = (ySurfaceInset >= 0) - ? std::min(ySurfaceInset, transformedLayerBounds.getHeight() / 2) - : 0; - - // inset while protecting from overflow TODO(b/161235021): What is going wrong - // in the overflow scenario? - { - int32_t tmp; - if (!__builtin_add_overflow(transformedLayerBounds.left, xSurfaceInset, &tmp)) - transformedLayerBounds.left = tmp; - if (!__builtin_sub_overflow(transformedLayerBounds.right, xSurfaceInset, &tmp)) - transformedLayerBounds.right = tmp; - if (!__builtin_add_overflow(transformedLayerBounds.top, ySurfaceInset, &tmp)) - transformedLayerBounds.top = tmp; - if (!__builtin_sub_overflow(transformedLayerBounds.bottom, ySurfaceInset, &tmp)) - transformedLayerBounds.bottom = tmp; - } - - // Compute the correct transform to send to input. This will allow it to transform the - // input coordinates from display space into window space. Therefore, it needs to use the - // final layer frame to create the inverse transform. Since surface insets are added later, - // along with the overflow, the best way to ensure we get the correct transform is to use - // the final frame calculated. - // 1. Take the original transform set on the window and get the inverse transform. This is - // used to get the final bounds in display space (ignorning the transform). Apply the - // inverse transform on the layerBounds to get the untransformed frame (in layer space) - // 2. Take the top and left of the untransformed frame to get the real position on screen. - // Apply the layer transform on top/left so it includes any scale or rotation. These will - // be the new translation values for the transform. - // 3. Update the translation of the original transform to the new translation values. - // 4. Send the inverse transform to input so the coordinates can be transformed back into - // window space. - ui::Transform inverseTransform = t.inverse(); - Rect nonTransformedBounds = inverseTransform.transform(transformedLayerBounds); - vec2 translation = t.transform(nonTransformedBounds.left, nonTransformedBounds.top); - ui::Transform inputTransform(t); - inputTransform.set(translation.x, translation.y); - info.transform = inputTransform.inverse(); - - // We need to send the layer bounds cropped to the screenbounds since the layer can be cropped. - // The frame should be the area the user sees on screen since it's used for occlusion - // detection. - transformedLayerBounds.intersect(screenBounds, &transformedLayerBounds); - info.frameLeft = transformedLayerBounds.left; - info.frameTop = transformedLayerBounds.top; - info.frameRight = transformedLayerBounds.right; - info.frameBottom = transformedLayerBounds.bottom; - - // Position the touchable region relative to frame screen location and restrict it to frame - // bounds. - info.touchableRegion = inputTransform.transform(info.touchableRegion); + tmpBounds = Rect::EMPTY_RECT; + } + + // InputDispatcher works in the display device's coordinate space. Here, we calculate the + // frame and transform used for the layer, which determines the bounds and the coordinate space + // within which the layer will receive input. + // + // The coordinate space within which each of the bounds are specified is explicitly documented + // in the variable name. For example "inputBoundsInLayer" is specified in layer space. A + // Transform converts one coordinate space to another, which is apparent in its naming. For + // example, "layerToDisplay" transforms layer space to display space. + // + // Coordinate space definitions: + // - display: The display device's coordinate space. Correlates to pixels on the display. + // - screen: The post-rotation coordinate space for the display, a.k.a. logical display space. + // - layer: The coordinate space of this layer. + // - input: The coordinate space in which this layer will receive input events. This could be + // different than layer space if a surfaceInset is used, which changes the origin + // of the input space. + const FloatRect inputBoundsInLayer = tmpBounds.toFloatRect(); + + // Clamp surface inset to the input bounds. + const auto surfaceInset = static_cast<float>(info.surfaceInset); + const float xSurfaceInset = + std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getWidth() / 2.f)); + const float ySurfaceInset = + std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getHeight() / 2.f)); + + // Apply the insets to the input bounds. + const FloatRect insetBoundsInLayer(inputBoundsInLayer.left + xSurfaceInset, + inputBoundsInLayer.top + ySurfaceInset, + inputBoundsInLayer.right - xSurfaceInset, + inputBoundsInLayer.bottom - ySurfaceInset); + + // Crop the input bounds to ensure it is within the parent's bounds. + const FloatRect croppedInsetBoundsInLayer = mBounds.intersect(insetBoundsInLayer); + + const ui::Transform layerToScreen = getInputTransform(); + const ui::Transform layerToDisplay = screenToDisplay * layerToScreen; + + const Rect roundedFrameInDisplay{layerToDisplay.transform(croppedInsetBoundsInLayer)}; + info.frameLeft = roundedFrameInDisplay.left; + info.frameTop = roundedFrameInDisplay.top; + info.frameRight = roundedFrameInDisplay.right; + info.frameBottom = roundedFrameInDisplay.bottom; + + ui::Transform inputToLayer; + inputToLayer.set(insetBoundsInLayer.left, insetBoundsInLayer.top); + const ui::Transform inputToDisplay = layerToDisplay * inputToLayer; + + // InputDispatcher expects a display-to-input transform. + info.transform = inputToDisplay.inverse(); + + // The touchable region is specified in the input coordinate space. Change it to display space. + info.touchableRegion = inputToDisplay.transform(info.touchableRegion); } void Layer::fillTouchOcclusionMode(WindowInfo& info) { diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 9e10b4f02f..3fecdeca79 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -1093,7 +1093,7 @@ private: void fillTouchOcclusionMode(gui::WindowInfo& info); // Fills in the frame and transform info for the gui::WindowInfo. - void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& displayTransform); + void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay); // Cached properties computed from drawing state // Effective transform taking into account parent transforms and any parent scaling, which is |