Merge changes from topics "sensor_conver", "sensorservice_aidl" am: a379349969 am: 3191a5120a
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/2292539
Change-Id: I02daf1ae5cfaa7cef12f1922601517607d3e4970
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index faf67fd..b1283eb 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -99,6 +99,8 @@
static constexpr const char* kDataMirrorCePath = "/data_mirror/data_ce";
static constexpr const char* kDataMirrorDePath = "/data_mirror/data_de";
+static constexpr const char* kMiscMirrorCePath = "/data_mirror/misc_ce";
+static constexpr const char* kMiscMirrorDePath = "/data_mirror/misc_de";
static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
@@ -3558,16 +3560,28 @@
std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
- return error("Failed to create CE mirror");
+ return error("Failed to create CE data mirror");
}
std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
if (fs_prepare_dir(mirrorVolDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
- return error("Failed to create DE mirror");
+ return error("Failed to create DE data mirror");
+ }
+
+ std::string mirrorVolMiscCePath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_));
+ if (fs_prepare_dir(mirrorVolMiscCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+ return error("Failed to create CE misc mirror");
+ }
+
+ std::string mirrorVolMiscDePath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_));
+ if (fs_prepare_dir(mirrorVolMiscDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+ return error("Failed to create DE misc mirror");
}
auto cePath = StringPrintf("%s/user", create_data_path(uuid_).c_str());
auto dePath = StringPrintf("%s/user_de", create_data_path(uuid_).c_str());
+ auto miscCePath = StringPrintf("%s/misc_ce", create_data_path(uuid_).c_str());
+ auto miscDePath = StringPrintf("%s/misc_de", create_data_path(uuid_).c_str());
if (access(cePath.c_str(), F_OK) != 0) {
return error("Cannot access CE path: " + cePath);
@@ -3575,6 +3589,12 @@
if (access(dePath.c_str(), F_OK) != 0) {
return error("Cannot access DE path: " + dePath);
}
+ if (access(miscCePath.c_str(), F_OK) != 0) {
+ return error("Cannot access misc CE path: " + cePath);
+ }
+ if (access(miscDePath.c_str(), F_OK) != 0) {
+ return error("Cannot access misc DE path: " + dePath);
+ }
struct stat ceStat, mirrorCeStat;
if (stat(cePath.c_str(), &ceStat) != 0) {
@@ -3602,6 +3622,21 @@
MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
return error("Failed to mount " + mirrorVolDePath);
}
+
+ // Mount misc CE mirror
+ if (TEMP_FAILURE_RETRY(mount(miscCePath.c_str(), mirrorVolMiscCePath.c_str(), NULL,
+ MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC,
+ nullptr)) == -1) {
+ return error("Failed to mount " + mirrorVolMiscCePath);
+ }
+
+ // Mount misc DE mirror
+ if (TEMP_FAILURE_RETRY(mount(miscDePath.c_str(), mirrorVolMiscDePath.c_str(), NULL,
+ MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC,
+ nullptr)) == -1) {
+ return error("Failed to mount " + mirrorVolMiscDePath);
+ }
+
return ok();
}
@@ -3624,6 +3659,8 @@
std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+ std::string mirrorMiscCeVolPath(StringPrintf("%s/%s", kMiscMirrorCePath, uuid_));
+ std::string mirrorMiscDeVolPath(StringPrintf("%s/%s", kMiscMirrorDePath, uuid_));
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
@@ -3648,6 +3685,29 @@
if (delete_dir_contents_and_dir(mirrorDeVolPath, true) != 0) {
res = error("Failed to delete " + mirrorDeVolPath);
}
+
+ // Unmount misc CE storage
+ if (TEMP_FAILURE_RETRY(umount(mirrorMiscCeVolPath.c_str())) != 0) {
+ if (errno != ENOENT) {
+ res = error(StringPrintf("Failed to umount %s %s", mirrorMiscCeVolPath.c_str(),
+ strerror(errno)));
+ }
+ }
+ if (delete_dir_contents_and_dir(mirrorMiscCeVolPath, true) != 0) {
+ res = error("Failed to delete " + mirrorMiscCeVolPath);
+ }
+
+ // Unmount misc DE storage
+ if (TEMP_FAILURE_RETRY(umount(mirrorMiscDeVolPath.c_str())) != 0) {
+ if (errno != ENOENT) {
+ res = error(StringPrintf("Failed to umount %s %s", mirrorMiscDeVolPath.c_str(),
+ strerror(errno)));
+ }
+ }
+ if (delete_dir_contents_and_dir(mirrorMiscDeVolPath, true) != 0) {
+ res = error("Failed to delete " + mirrorMiscDeVolPath);
+ }
+
return res;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 22dd866..6dc01b9 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -921,7 +921,8 @@
// Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
// and the middle part without rounded corners.
- const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
+ const int32_t radius = ceil(
+ (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) / 2.0);
const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
setScissor(topRect);
drawMesh(mesh);
@@ -1266,23 +1267,24 @@
const half3 solidColor = layer.source.solidColor;
const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+ const float radius =
+ (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) /
+ 2.0f;
// Buffer sources will have a black solid color ignored in the shader,
// so in that scenario the solid color passed here is arbitrary.
- setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
- layer.geometry.roundedCornersRadius);
+ setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, radius);
if (layer.disableBlending) {
glDisable(GL_BLEND);
}
setSourceDataSpace(layer.sourceDataspace);
if (layer.shadow.length > 0.0f) {
- handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
- layer.shadow);
+ handleShadow(layer.geometry.boundaries, radius, layer.shadow);
}
// We only want to do a special handling for rounded corners when having rounded corners
// is the only reason it needs to turn on blending, otherwise, we handle it like the
// usual way since it needs to turn on blending anyway.
- else if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ else if (radius > 0.0 && color.a >= 1.0f && isOpaque) {
handleRoundedCorners(display, layer, mesh);
} else {
drawMesh(mesh);
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 154e526..b3a617c 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -87,7 +87,7 @@
// rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
// in local layer coordinate space, so we have to take the layer transform into account when
// walking up the tree.
- float roundedCornersRadius = 0.0;
+ vec2 roundedCornersRadius = vec2(0.0f, 0.0f);
// Rectangle within which corners will be rounded.
FloatRect roundedCornersCrop = FloatRect();
@@ -258,7 +258,8 @@
PrintTo(settings.boundaries, os);
*os << "\n .positionTransform = ";
PrintMatrix(settings.positionTransform, os);
- *os << "\n .roundedCornersRadius = " << settings.roundedCornersRadius;
+ *os << "\n .roundedCornersRadiusX = " << settings.roundedCornersRadius.x;
+ *os << "\n .roundedCornersRadiusY = " << settings.roundedCornersRadius.y;
*os << "\n .roundedCornersCrop = ";
PrintTo(settings.roundedCornersCrop, os);
*os << "\n}";
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index f3064f3..c39f0a9 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -66,7 +66,7 @@
Geometry{
.boundaries = rect,
.roundedCornersCrop = rect,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
},
// drawShadow ignores alpha
.shadow =
@@ -87,7 +87,7 @@
Geometry{
.boundaries = smallerRect,
.roundedCornersCrop = rect,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
},
.source =
PixelSource{
@@ -148,7 +148,7 @@
// In reduced shader mode, all non-zero round rect radii get the same code path.
for (float roundedCornersRadius : {0.0f, 50.0f}) {
// roundedCornersCrop is always set, but the radius triggers the behavior
- layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
for (bool isOpaque : {true, false}) {
layer.source.buffer.isOpaque = isOpaque;
for (auto alpha : {half(.2f), half(1.0f)}) {
@@ -181,7 +181,7 @@
for (auto transform : {mat4(), kScaleAndTranslate}) {
layer.geometry.positionTransform = transform;
for (float roundedCornersRadius : {0.0f, 50.f}) {
- layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
auto layers = std::vector<LayerSettings>{layer};
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
base::unique_fd());
@@ -238,7 +238,7 @@
.geometry =
Geometry{
.boundaries = rect,
- .roundedCornersRadius = 27, // larger than the 20 above.
+ .roundedCornersRadius = {27.f, 27.f},
.roundedCornersCrop =
FloatRect(0, 0, displayRect.width(), displayRect.height()),
},
@@ -275,9 +275,9 @@
// larger than the layer bounds.
.positionTransform = kFlip,
.boundaries = rect,
- .roundedCornersRadius = 94.2551,
- .roundedCornersCrop = FloatRect(
- -93.75, 0, displayRect.width() + 93.75, displayRect.height()),
+ .roundedCornersRadius = {94.2551f, 94.2551f},
+ .roundedCornersCrop = FloatRect(-93.75, 0, displayRect.width() + 93.75,
+ displayRect.height()),
},
.source = PixelSource{.buffer =
Buffer{
@@ -307,10 +307,11 @@
// the boundaries have to be smaller than the rounded crop so that
// clipRRect is used instead of drawRRect
.boundaries = small,
- .roundedCornersRadius = 50.f,
+ .roundedCornersRadius = {50.f, 50.f},
.roundedCornersCrop = rect,
},
- .source = PixelSource{
+ .source =
+ PixelSource{
.solidColor = half3(0.f, 0.f, 0.f),
},
.sourceDataspace = kDestDataSpace,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 97271cb..0caa9f2 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -1276,7 +1276,7 @@
* produce the insected roundRect. If false, the returned state of the radii param is undefined.
*/
static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
- const SkRect& insetCrop, float cornerRadius,
+ const SkRect& insetCrop, const vec2& cornerRadius,
SkVector radii[4]) {
const bool leftEqual = bounds.fLeft == crop.fLeft;
const bool topEqual = bounds.fTop == crop.fTop;
@@ -1288,8 +1288,8 @@
// In particular the round rect implementation will scale the value of all corner radii
// if the sum of the radius along any edge is greater than the length of that edge.
// See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
- const bool requiredWidth = bounds.width() > (cornerRadius * 2);
- const bool requiredHeight = bounds.height() > (cornerRadius * 2);
+ const bool requiredWidth = bounds.width() > (cornerRadius.x * 2);
+ const bool requiredHeight = bounds.height() > (cornerRadius.y * 2);
if (!requiredWidth || !requiredHeight) {
return false;
}
@@ -1298,7 +1298,7 @@
// contained within the cropped shape and does not need rounded.
// compute the UpperLeft corner radius
if (leftEqual && topEqual) {
- radii[0].set(cornerRadius, cornerRadius);
+ radii[0].set(cornerRadius.x, cornerRadius.y);
} else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
(topEqual && bounds.fLeft >= insetCrop.fLeft)) {
radii[0].set(0, 0);
@@ -1307,7 +1307,7 @@
}
// compute the UpperRight corner radius
if (rightEqual && topEqual) {
- radii[1].set(cornerRadius, cornerRadius);
+ radii[1].set(cornerRadius.x, cornerRadius.y);
} else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
(topEqual && bounds.fRight <= insetCrop.fRight)) {
radii[1].set(0, 0);
@@ -1316,7 +1316,7 @@
}
// compute the BottomRight corner radius
if (rightEqual && bottomEqual) {
- radii[2].set(cornerRadius, cornerRadius);
+ radii[2].set(cornerRadius.x, cornerRadius.y);
} else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
(bottomEqual && bounds.fRight <= insetCrop.fRight)) {
radii[2].set(0, 0);
@@ -1325,7 +1325,7 @@
}
// compute the BottomLeft corner radius
if (leftEqual && bottomEqual) {
- radii[3].set(cornerRadius, cornerRadius);
+ radii[3].set(cornerRadius.x, cornerRadius.y);
} else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
(bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
radii[3].set(0, 0);
@@ -1338,22 +1338,22 @@
inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
const FloatRect& cropRect,
- const float cornerRadius) {
+ const vec2& cornerRadius) {
const SkRect bounds = getSkRect(boundsRect);
const SkRect crop = getSkRect(cropRect);
SkRRect clip;
- if (cornerRadius > 0) {
+ if (cornerRadius.x > 0 && cornerRadius.y > 0) {
// it the crop and the bounds are equivalent or there is no crop then we don't need a clip
if (bounds == crop || crop.isEmpty()) {
- return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip};
+ return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip};
}
// This makes an effort to speed up common, simple bounds + clip combinations by
// converting them to a single RRect draw. It is possible there are other cases
// that can be converted.
if (crop.contains(bounds)) {
- const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+ const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y);
if (insetCrop.contains(bounds)) {
return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
}
@@ -1367,7 +1367,7 @@
}
// we didn't hit any of our fast paths so set the clip to the cropRect
- clip.setRectXY(crop, cornerRadius, cornerRadius);
+ clip.setRectXY(crop, cornerRadius.x, cornerRadius.y);
}
// if we hit this point then we either don't have rounded corners or we are going to rely
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 5ef9944..68c3363 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -90,7 +90,8 @@
inline SkRect getSkRect(const FloatRect& layer);
inline SkRect getSkRect(const Rect& layer);
inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
- const FloatRect& crop, float cornerRadius);
+ const FloatRect& crop,
+ const vec2& cornerRadius);
inline bool layerHasBlur(const LayerSettings& layer, bool colorTransformModifiesAlpha);
inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7c70a74..8889f76 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -444,7 +444,9 @@
const ubyte4& backgroundColor) {
const Rect casterRect(castingLayer.geometry.boundaries);
Region casterRegion = Region(casterRect);
- const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+ const float casterCornerRadius = (castingLayer.geometry.roundedCornersRadius.x +
+ castingLayer.geometry.roundedCornersRadius.y) /
+ 2.0;
if (casterCornerRadius > 0.0f) {
// ignore the corners if a corner radius is set
Rect cornerRect(casterCornerRadius, casterCornerRadius);
@@ -1129,7 +1131,7 @@
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
- layer.geometry.roundedCornersRadius = 5.0f;
+ layer.geometry.roundedCornersRadius = {5.0f, 5.0f};
layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0f;
@@ -2131,7 +2133,7 @@
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
renderengine::LayerSettings castingLayer;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
- castingLayer.geometry.roundedCornersRadius = 3.0f;
+ castingLayer.geometry.roundedCornersRadius = {3.0f, 3.0f};
castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
renderengine::ShadowSettings settings =
@@ -2219,7 +2221,8 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
+
redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2231,7 +2234,7 @@
renderengine::LayerSettings greenLayer;
greenLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
greenLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- greenLayer.geometry.roundedCornersRadius = 5.0f;
+ greenLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
// Bottom right corner is not going to be rounded.
greenLayer.geometry.roundedCornersCrop =
Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3, DEFAULT_DISPLAY_HEIGHT,
@@ -2268,7 +2271,7 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
- redLayer.geometry.roundedCornersRadius = 5.0f;
+ redLayer.geometry.roundedCornersRadius = {5.0f, 5.0f};
redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2313,7 +2316,7 @@
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32);
- redLayer.geometry.roundedCornersRadius = 64;
+ redLayer.geometry.roundedCornersRadius = {64.0f, 64.0f};
redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128);
// Red background.
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
@@ -2334,6 +2337,49 @@
expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255);
}
+TEST_P(RenderEngineTest, testRoundedCornersXY) {
+ if (GetParam()->type() != renderengine::RenderEngine::RenderEngineType::SKIA_GL) {
+ GTEST_SKIP();
+ }
+
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings redLayer;
+ redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ redLayer.geometry.roundedCornersRadius = {5.0f, 20.0f};
+ redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect();
+ // Red background.
+ redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ redLayer.alpha = 1.0f;
+
+ layers.push_back(redLayer);
+
+ invokeDraw(settings, layers);
+
+ // Due to roundedCornersRadius, the corners are untouched.
+ expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0);
+
+ // Y-axis draws a larger radius, check that its untouched as well
+ expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 5), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 5), 0, 0, 0, 0);
+ expectBufferColor(Point(0, 5), 0, 0, 0, 0);
+
+ // middle should be red
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255);
+}
+
TEST_P(RenderEngineTest, testClear) {
initializeRenderEngine();
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 41a8426..cc323ad 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -48,11 +48,8 @@
delete[] mSlots;
mSlots = new Slot[slotCount];
-}
-void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
- // Unfortunately there is no way to read the initial contents of the slots.
- // So when we reset the accumulator, we must assume they are all zeroes.
+ mCurrentSlot = -1;
if (mUsingSlotsProtocol) {
// Query the driver for the current slot index and use it as the initial slot
// before we start reading events from the device. It is possible that the
@@ -64,24 +61,22 @@
// This can cause the touch point to "jump", but at least there will be
// no stuck touches.
int32_t initialSlot;
- status_t status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
- if (status) {
- ALOGD("Could not retrieve current multitouch slot index. status=%d", status);
- initialSlot = -1;
+ if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
+ status == OK) {
+ mCurrentSlot = initialSlot;
+ } else {
+ ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
}
- clearSlots(initialSlot);
- } else {
- clearSlots(-1);
}
}
-void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
+void MultiTouchMotionAccumulator::resetSlots() {
if (mSlots) {
for (size_t i = 0; i < mSlotCount; i++) {
mSlots[i].clear();
}
}
- mCurrentSlot = initialSlot;
+ mCurrentSlot = -1;
}
void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
@@ -169,7 +164,7 @@
void MultiTouchMotionAccumulator::finishSync() {
if (!mUsingSlotsProtocol) {
- clearSlots(-1);
+ resetSlots();
}
}
@@ -230,10 +225,12 @@
MultiTouchInputMapper::~MultiTouchInputMapper() {}
void MultiTouchInputMapper::reset(nsecs_t when) {
- mMultiTouchMotionAccumulator.reset(getDeviceContext());
-
- mPointerIdBits.clear();
-
+ // The evdev multi-touch protocol does not allow userspace applications to query the initial or
+ // current state of the pointers at any time. This means if we clear our accumulated state when
+ // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
+ // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
+ // rebuilding the state from scratch, we work around this kernel API limitation by never
+ // fully clearing any state specific to the multi-touch protocol.
TouchInputMapper::reset(when);
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index b7c3457..e7d9350 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -71,7 +71,6 @@
~MultiTouchMotionAccumulator();
void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol);
- void reset(InputDeviceContext& deviceContext);
void process(const RawEvent* rawEvent);
void finishSync();
bool hasStylus() const;
@@ -86,7 +85,7 @@
bool mUsingSlotsProtocol;
bool mHaveStylus;
- void clearSlots(int32_t initialSlot);
+ void resetSlots();
void warnIfNotInUse(const RawEvent& event, const Slot& slot);
};
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 2ddacef..ed3b0ec 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1473,6 +1473,10 @@
}
void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
+ if (mDeviceMode == DeviceMode::DISABLED) {
+ // Only save the last pending state when the device is disabled.
+ mRawStatesPending.clear();
+ }
// Push a new state.
mRawStatesPending.emplace_back();
@@ -3522,6 +3526,8 @@
void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
bool down, bool hovering) {
+ LOG_ALWAYS_FATAL_IF(mDeviceMode != DeviceMode::POINTER,
+ "%s cannot be used when the device is not in POINTER mode.", __func__);
int32_t metaState = getContext()->getGlobalMetaState();
if (down || hovering) {
@@ -3644,6 +3650,10 @@
if (down || hovering) {
mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords);
mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties);
+ mPointerSimple.displayId = displayId;
+ mPointerSimple.source = mSource;
+ mPointerSimple.lastCursorX = xCursorPosition;
+ mPointerSimple.lastCursorY = yCursorPosition;
} else {
mPointerSimple.reset();
}
@@ -3653,7 +3663,23 @@
mPointerSimple.currentCoords.clear();
mPointerSimple.currentProperties.clear();
- dispatchPointerSimple(when, readTime, policyFlags, false, false);
+ if (mPointerSimple.down || mPointerSimple.hovering) {
+ int32_t metaState = getContext()->getGlobalMetaState();
+ NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
+ mPointerSimple.source, mPointerSimple.displayId, policyFlags,
+ AMOTION_EVENT_ACTION_CANCEL, 0, AMOTION_EVENT_FLAG_CANCELED,
+ metaState, mLastRawState.buttonState, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+ &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+ mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
+ mPointerSimple.downTime,
+ /* videoFrames */ {});
+ getListener().notifyMotion(&args);
+ if (mPointerController != nullptr) {
+ mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
+ }
+ }
+ mPointerSimple.reset();
}
void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index c948f56..2937bf8 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -327,6 +327,8 @@
int32_t rawVScroll;
int32_t rawHScroll;
+ explicit inline RawState() { clear(); }
+
void copyFrom(const RawState& other) {
when = other.when;
readTime = other.readTime;
@@ -712,6 +714,12 @@
// Time the pointer last went down.
nsecs_t downTime;
+ // Values reported for the last pointer event.
+ uint32_t source;
+ int32_t displayId;
+ float lastCursorX;
+ float lastCursorY;
+
void reset() {
currentCoords.clear();
currentProperties.clear();
@@ -720,6 +728,10 @@
down = false;
hovering = false;
downTime = 0;
+ source = 0;
+ displayId = ADISPLAY_ID_NONE;
+ lastCursorX = 0.f;
+ lastCursorY = 0.f;
}
} mPointerSimple;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 3afa52c..03fbf07 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -111,6 +111,13 @@
return arg.pointerCoords[0].getX() == x && arg.pointerCoords[0].getY();
}
+MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
+ const auto argToolType = arg.pointerProperties[0].toolType;
+ *result_listener << "expected tool type " << motionToolTypeToString(toolType) << ", but got "
+ << motionToolTypeToString(argToolType);
+ return argToolType == toolType;
+}
+
template<typename T>
static inline T min(T a, T b) {
return a < b ? a : b;
@@ -6787,6 +6794,82 @@
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
}
+TEST_F(SingleTouchInputMapperTest, Reset_RecreatesTouchState) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION | PRESSURE);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ NotifyMotionArgs motionArgs;
+
+ // Set the initial state for the touch pointer.
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 100);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_Y, 200);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_PRESSURE, RAW_PRESSURE_MAX);
+ mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1);
+
+ // Reset the mapper. When the mapper is reset, we expect it to attempt to recreate the touch
+ // state by reading the current axis values.
+ mapper.reset(ARBITRARY_TIME);
+
+ // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
+ // the recreated touch state to generate a down event.
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, WhenViewportActiveStatusChanged_PointerGestureIsReset) {
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ fakePointerController->setPosition(100, 200);
+ fakePointerController->setButtonState(0);
+ mFakePolicy->setPointerController(fakePointerController);
+
+ addConfigurationProperty("touch.deviceType", "pointer");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareButtons();
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ // Start a stylus gesture.
+ processKey(mapper, BTN_TOOL_PEN, 1);
+ processDown(mapper, 100, 200);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithAction(AMOTION_EVENT_ACTION_DOWN),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ // TODO(b/257078296): Pointer mode generates extra event.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithAction(AMOTION_EVENT_ACTION_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Make the viewport inactive. This will put the device in disabled mode, and the ongoing stylus
+ // gesture should be disabled.
+ auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ viewport->isActive = false;
+ mFakePolicy->updateViewport(*viewport);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithAction(AMOTION_EVENT_ACTION_CANCEL),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ // TODO(b/257078296): Pointer mode generates extra event.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ AllOf(WithAction(AMOTION_EVENT_ACTION_CANCEL),
+ WithSource(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
TEST_F(SingleTouchInputMapperTest,
Process_WhenViewportDisplayIdChanged_TouchIsCanceledAndDeviceIsReset) {
addConfigurationProperty("touch.deviceType", "touchScreen");
@@ -6848,10 +6931,17 @@
// No events are generated while the viewport is inactive.
processMove(mapper, 101, 201);
processSync(mapper);
- processDown(mapper, 102, 202);
+ processUp(mapper);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ // Start a new gesture while the viewport is still inactive.
+ processDown(mapper, 300, 400);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_X, 300);
+ mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_Y, 400);
+ mFakeEventHub->setScanCodeState(EVENTHUB_ID, BTN_TOUCH, 1);
+ processSync(mapper);
+
// Make the viewport active again. The device should resume processing events.
viewport->isActive = true;
mFakePolicy->updateViewport(*viewport);
@@ -6861,8 +6951,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
- // Start a new gesture.
- processDown(mapper, 100, 200);
+ // In the next sync, the touch state that was recreated when the device was reset is reported.
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
@@ -9397,6 +9486,80 @@
ASSERT_EQ(uint32_t(1), motionArgs.pointerCount);
}
+TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT | PRESSURE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // First finger down.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 100, 200);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+
+ // Second finger down.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, 300, 400);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
+
+ // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be
+ // preserved. Resetting should not generate any events.
+ mapper.reset(ARBITRARY_TIME);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
+ // the existing touch state to generate a down event.
+ processPosition(mapper, 301, 302);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ACTION_POINTER_1_DOWN, motionArgs.action);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT | PRESSURE);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // First finger touches down and releases.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 100, 200);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+
+ // Reset the mapper. When the mapper is reset, we expect it to restore the latest
+ // raw state where no pointers are down.
+ mapper.reset(ARBITRARY_TIME);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Send an empty sync frame. Since there are no pointers, no events are generated.
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
// --- MultiTouchInputMapperTest_ExternalDevice ---
class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index df721cd..428c19f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -152,6 +152,8 @@
virtual const compositionengine::CompositionEngine& getCompositionEngine() const = 0;
virtual void dumpState(std::string& out) const = 0;
+ bool mustRecompose() const;
+
private:
void dirtyEntireOutput();
compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
@@ -170,6 +172,9 @@
std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
std::unique_ptr<planner::Planner> mPlanner;
std::unique_ptr<HwcAsyncWorker> mHwComposerAsyncWorker;
+
+ // Whether the content must be recomposed this frame.
+ bool mMustRecompose = false;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 1ec6449..163d9a3 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -426,7 +426,7 @@
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
// 2) There is work to be done (the dirty region isn't empty)
- if (GpuVirtualDisplayId::tryCast(mId) && getDirtyRegion().isEmpty()) {
+ if (GpuVirtualDisplayId::tryCast(mId) && !mustRecompose()) {
ALOGV("Skipping display composition");
return;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index b724daa..d0c5803 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -977,17 +977,17 @@
// frame, then nothing more until we get new layers.
// - When a display is created with a private layer stack, we won't
// emit any black frames until a layer is added to the layer stack.
- const bool mustRecompose = dirty && !(empty && wasEmpty);
+ mMustRecompose = dirty && !(empty && wasEmpty);
const char flagPrefix[] = {'-', '+'};
static_cast<void>(flagPrefix);
- ALOGV_IF("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __FUNCTION__,
- mustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty],
- flagPrefix[empty], flagPrefix[wasEmpty]);
+ ALOGV("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __func__,
+ mMustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty],
+ flagPrefix[empty], flagPrefix[wasEmpty]);
- mRenderSurface->beginFrame(mustRecompose);
+ mRenderSurface->beginFrame(mMustRecompose);
- if (mustRecompose) {
+ if (mMustRecompose) {
outputState.lastCompositionHadVisibleLayers = !empty;
}
}
@@ -1590,5 +1590,9 @@
mRenderSurface->prepareFrame(state.usesClientComposition, state.usesDeviceComposition);
}
+bool Output::mustRecompose() const {
+ return mMustRecompose;
+}
+
} // namespace impl
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 344fea3..5369642 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -971,16 +971,40 @@
// We expect no calls to queueBuffer if composition was skipped.
EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+ EXPECT_CALL(*renderSurface, beginFrame(false));
gpuDisplay->editState().isEnabled = true;
gpuDisplay->editState().usesClientComposition = false;
gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+ gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
+ gpuDisplay->beginFrame();
gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
}
-TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
+TEST_F(DisplayFinishFrameTest, skipsCompositionIfEmpty) {
+ auto args = getDisplayCreationArgsForGpuVirtualDisplay();
+ std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
+
+ mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+ gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+ // We expect no calls to queueBuffer if composition was skipped.
+ EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+ EXPECT_CALL(*renderSurface, beginFrame(false));
+
+ gpuDisplay->editState().isEnabled = true;
+ gpuDisplay->editState().usesClientComposition = false;
+ gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
+ gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
+ gpuDisplay->editState().lastCompositionHadVisibleLayers = false;
+
+ gpuDisplay->beginFrame();
+ gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
+}
+
+TEST_F(DisplayFinishFrameTest, performsCompositionIfDirtyAndNotEmpty) {
auto args = getDisplayCreationArgsForGpuVirtualDisplay();
std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
@@ -989,11 +1013,15 @@
// We expect a single call to queueBuffer when composition is not skipped.
EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+ EXPECT_CALL(*renderSurface, beginFrame(true));
gpuDisplay->editState().isEnabled = true;
gpuDisplay->editState().usesClientComposition = false;
gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
+ gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
+
+ gpuDisplay->beginFrame();
gpuDisplay->finishFrame({}, std::move(mResultWithBuffer));
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0c4b012..c2b0a11 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -406,7 +406,7 @@
const auto& drawingState{getDrawingState()};
const auto alpha = static_cast<float>(getAlpha());
const bool opaque = isOpaque(drawingState);
- const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+ const bool usesRoundedCorners = hasRoundedCorners();
auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
if (!opaque || alpha != 1.0f) {
@@ -482,7 +482,7 @@
compositionState->hasProtectedContent = isProtected();
compositionState->dimmingEnabled = isDimmingEnabled();
- const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+ const bool usesRoundedCorners = hasRoundedCorners();
compositionState->isOpaque =
isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
@@ -1917,27 +1917,22 @@
const auto& parent = mDrawingParent.promote();
if (parent != nullptr) {
parentSettings = parent->getRoundedCornerState();
- if (parentSettings.radius > 0) {
+ if (parentSettings.hasRoundedCorners()) {
ui::Transform t = getActiveTransform(getDrawingState());
t = t.inverse();
parentSettings.cropRect = t.transform(parentSettings.cropRect);
- // The rounded corners shader only accepts 1 corner radius for performance reasons,
- // but a transform matrix can define horizontal and vertical scales.
- // Let's take the average between both of them and pass into the shader, practically we
- // never do this type of transformation on windows anyway.
- auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]);
- auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]);
- parentSettings.radius *= (scaleX + scaleY) / 2.0f;
+ parentSettings.radius.x *= t.getScaleX();
+ parentSettings.radius.y *= t.getScaleY();
}
}
// Get layer settings
Rect layerCropRect = getCroppedBufferSize(getDrawingState());
- const float radius = getDrawingState().cornerRadius;
+ const vec2 radius(getDrawingState().cornerRadius, getDrawingState().cornerRadius);
RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius);
- const bool layerSettingsValid = layerSettings.radius > 0 && layerCropRect.isValid();
+ const bool layerSettingsValid = layerSettings.hasRoundedCorners() && layerCropRect.isValid();
- if (layerSettingsValid && parentSettings.radius > 0) {
+ if (layerSettingsValid && parentSettings.hasRoundedCorners()) {
// If the parent and the layer have rounded corner settings, use the parent settings if the
// parent crop is entirely inside the layer crop.
// This has limitations and cause rendering artifacts. See b/200300845 for correct fix.
@@ -1951,7 +1946,7 @@
}
} else if (layerSettingsValid) {
return layerSettings;
- } else if (parentSettings.radius > 0) {
+ } else if (parentSettings.hasRoundedCorners()) {
return parentSettings;
}
return {};
@@ -2072,7 +2067,8 @@
layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
- layerInfo->set_corner_radius(getRoundedCornerState().radius);
+ layerInfo->set_corner_radius(
+ (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0);
layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
layerInfo->set_is_trusted_overlay(isTrustedOverlay());
LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c547da0..200baf0 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -134,13 +134,14 @@
struct RoundedCornerState {
RoundedCornerState() = default;
- RoundedCornerState(FloatRect cropRect, float radius)
+ RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
: cropRect(cropRect), radius(radius) {}
// Rounded rectangle in local layer coordinate space.
FloatRect cropRect = FloatRect();
// Radius of the rounded rectangle.
- float radius = 0.0f;
+ vec2 radius;
+ bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
};
using FrameRate = scheduler::LayerInfo::FrameRate;
@@ -596,7 +597,7 @@
// corner crop does not intersect with its own rounded corner crop.
virtual RoundedCornerState getRoundedCornerState() const;
- bool hasRoundedCorners() const override { return getRoundedCornerState().radius > .0f; }
+ bool hasRoundedCorners() const override { return getRoundedCornerState().hasRoundedCorners(); }
virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
/**
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index bbfedc7..fbc532e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -664,7 +664,8 @@
EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
EXPECT_EQ(false, layer.source.buffer.isOpaque);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
return resultFuture;
@@ -714,7 +715,8 @@
EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2]),
layer.source.solidColor);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
return resultFuture;
@@ -792,7 +794,8 @@
const renderengine::LayerSettings layer = layerSettings.back();
EXPECT_THAT(layer.source.buffer.buffer, IsNull());
EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
- EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
EXPECT_EQ(1.0f, layer.alpha);
return resultFuture;