Merge "Ensure that LayerSettings equality check includes blurRegionTransform." into sc-dev
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index f6c2e55..9881371 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -526,6 +526,9 @@
* callback timings, and changes to the time interval at which the system releases buffers back to
* the application.
*
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
* \param frameRate is the intended frame rate of this surface, in frames per second. 0 is a special
* value that indicates the app will accept the system's choice for the display frame rate, which is
* the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 3865ba5..f0e1c4d 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -300,6 +300,9 @@
* this ANativeWindow is consumed by something other than the system compositor,
* e.g. a media codec, this call has no effect.
*
+ * You can register for changes in the refresh rate using
+ * \a AChoreographer_registerRefreshRateCallback.
+ *
* Available since API level 31.
*
* \param window pointer to an ANativeWindow object.
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 3e3649e..540e36a 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -366,6 +366,10 @@
return mProtectedEGLContext != EGL_NO_CONTEXT;
}
+GrDirectContext* SkiaGLRenderEngine::getActiveGrContext() const {
+ return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get();
+}
+
bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
if (useProtectedContext == mInProtectedContext) {
return true;
@@ -373,6 +377,12 @@
if (useProtectedContext && !supportsProtectedContent()) {
return false;
}
+
+ // release any scratch resources before switching into a new mode
+ if (getActiveGrContext()) {
+ getActiveGrContext()->purgeUnlockedResources(true);
+ }
+
const EGLSurface surface =
useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
@@ -380,6 +390,11 @@
if (success) {
mInProtectedContext = useProtectedContext;
+ // given that we are sharing the same thread between two GrContexts we need to
+ // make sure that the thread state is reset when switching between the two.
+ if (getActiveGrContext()) {
+ getActiveGrContext()->resetContext();
+ }
}
return success;
}
@@ -490,18 +505,23 @@
if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
return;
}
+ // we currently don't attempt to map a buffer if the buffer contains protected content
+ // because GPU resources for protected buffers is much more limited.
+ const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
+ if (isProtectedBuffer) {
+ return;
+ }
ATRACE_CALL();
// We need to switch the currently bound context if the buffer is protected but the current
// context is not. The current state must then be restored after the buffer is cached.
const bool protectedContextState = mInProtectedContext;
- if (!useProtectedContext(protectedContextState ||
- (buffer->getUsage() & GRALLOC_USAGE_PROTECTED))) {
+ if (!useProtectedContext(protectedContextState || isProtectedBuffer)) {
ALOGE("Attempting to cache a buffer into a different context than what is currently bound");
return;
}
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ auto grContext = getActiveGrContext();
auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
std::lock_guard<std::mutex> lock(mRenderingMutex);
@@ -509,7 +529,7 @@
if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
- std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(),
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext,
buffer->toAHardwareBuffer(),
isRenderable);
cache.insert({buffer->getId(), imageTextureRef});
@@ -667,6 +687,17 @@
canvas->drawRect(stretchBounds, paint);
}
+static SkRRect getBlurRRect(const BlurRegion& region) {
+ const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom);
+ const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL),
+ SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR),
+ SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR),
+ SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)};
+ SkRRect roundedRect;
+ roundedRect.setRectRadii(rect, radii);
+ return roundedRect;
+}
+
status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
@@ -695,7 +726,7 @@
validateOutputBufferUsage(buffer->getBuffer());
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ auto grContext = getActiveGrContext();
auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef;
@@ -703,14 +734,15 @@
surfaceTextureRef = it->second;
} else {
surfaceTextureRef =
- std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(),
- buffer->getBuffer()->toAHardwareBuffer(), true);
+ std::make_shared<AutoBackendTexture::LocalRef>(grContext,
+ buffer->getBuffer()
+ ->toAHardwareBuffer(),
+ true);
}
const ui::Dataspace dstDataspace =
mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
- sk_sp<SkSurface> dstSurface =
- surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext.get());
+ sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext);
SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
if (dstCanvas == nullptr) {
@@ -840,7 +872,7 @@
// Layers have a local transform that should be applied to them
canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
- const auto bounds = getSkRect(layer->geometry.boundaries);
+ const auto [bounds, roundRectClip] = getBoundsAndClip(layer);
if (mBlurFilter && layerHasBlur(layer)) {
std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
@@ -850,32 +882,40 @@
blurInput = activeSurface->makeImageSnapshot();
}
// rect to be blurred in the coordinate space of blurInput
- const auto blurRect = canvas->getTotalMatrix().mapRect(bounds);
+ const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect());
+
+ // if the clip needs to be applied then apply it now and make sure
+ // it is restored before we attempt to draw any shadows.
+ SkAutoCanvasRestore acr(canvas, true);
+ if (!roundRectClip.isEmpty()) {
+ canvas->clipRRect(roundRectClip, true);
+ }
// TODO(b/182216890): Filter out empty layers earlier
if (blurRect.width() > 0 && blurRect.height() > 0) {
if (layer->backgroundBlurRadius > 0) {
ATRACE_NAME("BackgroundBlur");
auto blurredImage =
- mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius,
- blurInput, blurRect);
+ mBlurFilter->generate(grContext, layer->backgroundBlurRadius, blurInput,
+ blurRect);
cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
- mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect,
- blurredImage, blurInput);
+ mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f,
+ blurRect, blurredImage, blurInput);
}
- SkAutoCanvasRestore acr(canvas, true);
+
canvas->concat(getSkM44(layer->blurRegionTransform).asM33());
for (auto region : layer->blurRegions) {
if (cachedBlurs[region.blurRadius] == nullptr) {
ATRACE_NAME("BlurRegion");
cachedBlurs[region.blurRadius] =
- mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput,
+ mBlurFilter->generate(grContext, region.blurRadius, blurInput,
blurRect);
}
- mBlurFilter->drawBlurRegion(canvas, region, blurRect,
+ mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius,
+ region.alpha, blurRect,
cachedBlurs[region.blurRadius], blurInput);
}
}
@@ -888,7 +928,7 @@
if (layer->shadow.length > 0) {
const auto rect = layer->geometry.roundedCornersRadius > 0
? getSkRect(layer->geometry.roundedCornersCrop)
- : bounds;
+ : bounds.rect();
// This would require a new parameter/flag to SkShadowUtils::DrawShadow
LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
@@ -928,7 +968,7 @@
// we didn't find anything in the cache then we intentionally did not cache this
// buffer's resources.
imageTextureRef = std::make_shared<
- AutoBackendTexture::LocalRef>(grContext.get(),
+ AutoBackendTexture::LocalRef>(grContext,
item.buffer->getBuffer()->toAHardwareBuffer(),
false);
}
@@ -937,7 +977,7 @@
imageTextureRef->makeImage(layerDataspace,
item.usePremultipliedAlpha ? kPremul_SkAlphaType
: kUnpremul_SkAlphaType,
- grContext.get());
+ grContext);
auto texMatrix = getSkM44(item.textureTransform).asM33();
// textureTansform was intended to be passed directly into a shader, so when
@@ -953,7 +993,7 @@
// The shader does not respect the translation, so we add it to the texture
// transform for the SkImage. This will make sure that the correct layer contents
// are drawn in the correct part of the screen.
- matrix.postTranslate(layer->geometry.boundaries.left, layer->geometry.boundaries.top);
+ matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop);
sk_sp<SkShader> shader;
@@ -1015,9 +1055,13 @@
paint.setColorFilter(displayColorTransform);
- if (layer->geometry.roundedCornersRadius > 0) {
+ if (!roundRectClip.isEmpty()) {
+ canvas->clipRRect(roundRectClip, true);
+ }
+
+ if (!bounds.isRect()) {
paint.setAntiAlias(true);
- canvas->drawRRect(getRoundedRect(layer), paint);
+ canvas->drawRRect(bounds, paint);
} else {
auto& stretchEffect = layer->stretchEffect;
// TODO (njawad) temporarily disable manipulation of geometry
@@ -1026,9 +1070,9 @@
// Keep the method call in a dead code path to make -Werror happy
// with unused methods
if (stretchEffect.hasEffect() && /* DISABLES CODE */ (false)) {
- drawStretch(bounds, stretchEffect, canvas, paint);
+ drawStretch(bounds.rect(), stretchEffect, canvas, paint);
} else {
- canvas->drawRect(bounds, paint);
+ canvas->drawRect(bounds.rect(), paint);
}
}
if (kFlushAfterEveryLayer) {
@@ -1077,25 +1121,76 @@
return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
}
-inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) {
- const auto rect = getSkRect(layer->geometry.roundedCornersCrop);
+inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(
+ const LayerSettings* layer) {
+ const auto bounds = getSkRect(layer->geometry.boundaries);
+ const auto crop = getSkRect(layer->geometry.roundedCornersCrop);
const auto cornerRadius = layer->geometry.roundedCornersRadius;
- return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius);
-}
-inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer) {
- const auto rect = getSkRect(layer->geometry.boundaries);
- const auto cornersRadius = layer->geometry.roundedCornersRadius;
- return BlurRegion{.blurRadius = static_cast<uint32_t>(layer->backgroundBlurRadius),
- .cornerRadiusTL = cornersRadius,
- .cornerRadiusTR = cornersRadius,
- .cornerRadiusBL = cornersRadius,
- .cornerRadiusBR = cornersRadius,
- .alpha = 1,
- .left = static_cast<int>(rect.fLeft),
- .top = static_cast<int>(rect.fTop),
- .right = static_cast<int>(rect.fRight),
- .bottom = static_cast<int>(rect.fBottom)};
+ SkRRect clip;
+ if (cornerRadius > 0) {
+ // it the crop and the bounds are equivalent then we don't need a clip
+ if (bounds == crop) {
+ return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), 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)) {
+ bool intersectionIsRoundRect = true;
+ // check each cropped corner to ensure that it exactly matches the crop or is full
+ SkVector radii[4];
+
+ const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
+
+ // compute the UpperLeft corner radius
+ if (bounds.fLeft == crop.fLeft && bounds.fTop == crop.fTop) {
+ radii[0].set(cornerRadius, cornerRadius);
+ } else if (bounds.fLeft > insetCrop.fLeft && bounds.fTop > insetCrop.fTop) {
+ radii[0].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the UpperRight corner radius
+ if (bounds.fRight == crop.fRight && bounds.fTop == crop.fTop) {
+ radii[1].set(cornerRadius, cornerRadius);
+ } else if (bounds.fRight < insetCrop.fRight && bounds.fTop > insetCrop.fTop) {
+ radii[1].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the BottomRight corner radius
+ if (bounds.fRight == crop.fRight && bounds.fBottom == crop.fBottom) {
+ radii[2].set(cornerRadius, cornerRadius);
+ } else if (bounds.fRight < insetCrop.fRight && bounds.fBottom < insetCrop.fBottom) {
+ radii[2].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+ // compute the BottomLeft corner radius
+ if (bounds.fLeft == crop.fLeft && bounds.fBottom == crop.fBottom) {
+ radii[3].set(cornerRadius, cornerRadius);
+ } else if (bounds.fLeft > insetCrop.fLeft && bounds.fBottom < insetCrop.fBottom) {
+ radii[3].set(0, 0);
+ } else {
+ intersectionIsRoundRect = false;
+ }
+
+ if (intersectionIsRoundRect) {
+ SkRRect intersectionBounds;
+ intersectionBounds.setRectRadii(bounds, radii);
+ return {intersectionBounds, clip};
+ }
+ }
+
+ // we didn't it any of our fast paths so set the clip to the cropRect
+ clip.setRectXY(crop, cornerRadius, cornerRadius);
+ }
+
+ // if we hit this point then we either don't have rounded corners or we are going to rely
+ // on the clip to round the corners for us
+ return {SkRRect::MakeRect(bounds), clip};
}
inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) {
@@ -1267,13 +1362,11 @@
const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
// start by resizing the current context
- auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
- grContext->setResourceCacheLimit(maxResourceBytes);
+ getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
// if it is possible to switch contexts then we will resize the other context
if (useProtectedContext(!mInProtectedContext)) {
- grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
- grContext->setResourceCacheLimit(maxResourceBytes);
+ getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
// reset back to the initial context that was active when this method was called
useProtectedContext(!mInProtectedContext);
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 98f5ee2..d9caf35 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -88,12 +88,12 @@
int hwcFormat, Protection protection);
inline SkRect getSkRect(const FloatRect& layer);
inline SkRect getSkRect(const Rect& layer);
- inline SkRRect getRoundedRect(const LayerSettings* layer);
- inline BlurRegion getBlurRegion(const LayerSettings* layer);
+ inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const LayerSettings* layer);
inline bool layerHasBlur(const LayerSettings* layer);
inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
inline SkPoint3 getSkPoint3(const vec3& vector);
+ inline GrDirectContext* getActiveGrContext() const;
base::unique_fd flush();
bool waitFence(base::unique_fd fenceFd);
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 5cba8fb..4ad6e94 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -139,23 +139,21 @@
return matrix;
}
-void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
+ const uint32_t blurRadius, const float blurAlpha,
const SkRect& blurRect, sk_sp<SkImage> blurredImage,
sk_sp<SkImage> input) {
ATRACE_CALL();
SkPaint paint;
- paint.setAlphaf(effectRegion.alpha);
- if (effectRegion.alpha == 1.0f) {
- paint.setBlendMode(SkBlendMode::kSrc);
- }
+ paint.setAlphaf(blurAlpha);
const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale);
SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
linearSampling, &blurMatrix);
- if (effectRegion.blurRadius < kMaxCrossFadeRadius) {
+ if (blurRadius < kMaxCrossFadeRadius) {
// For sampling Skia's API expects the inverse of what logically seems appropriate. In this
// case you might expect the matrix to simply be the canvas matrix.
SkMatrix inputMatrix;
@@ -168,30 +166,21 @@
blurBuilder.child("originalInput") =
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
inputMatrix);
- blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius;
+ blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius;
paint.setShader(blurBuilder.makeShader(nullptr, true));
} else {
paint.setShader(blurShader);
}
- // TODO we should AA at least the drawRoundRect which would mean no SRC blending
- // TODO this round rect calculation doesn't match the one used to draw in RenderEngine
- auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
- effectRegion.bottom);
-
- if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
- effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
- const SkVector radii[4] =
- {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
- SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
- SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
- SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
- SkRRect roundedRect;
- roundedRect.setRectRadii(rect, radii);
- canvas->drawRRect(roundedRect, paint);
+ if (effectRegion.isRect()) {
+ if (blurAlpha == 1.0f) {
+ paint.setBlendMode(SkBlendMode::kSrc);
+ }
+ canvas->drawRect(effectRegion.rect(), paint);
} else {
- canvas->drawRect(rect, paint);
+ paint.setAntiAlias(true);
+ canvas->drawRRect(effectRegion, paint);
}
}
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 731ba11..a8e6dd7 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -20,7 +20,6 @@
#include <SkImage.h>
#include <SkRuntimeEffect.h>
#include <SkSurface.h>
-#include <ui/BlurRegion.h>
using namespace std;
@@ -52,8 +51,19 @@
sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
- void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect,
- sk_sp<SkImage> blurredImage, sk_sp<SkImage> input);
+ /**
+ * Draw the blurred content (from the generate method) into the canvas.
+ * @param canvas is the destination/output for the blur
+ * @param effectRegion the RoundRect in canvas coordinates that determines the blur coverage
+ * @param blurRadius radius of the blur used to determine the intensity of the crossfade effect
+ * @param blurAlpha alpha value applied to the effectRegion when the blur is drawn
+ * @param blurRect bounds of the blurredImage translated into canvas coordinates
+ * @param blurredImage down-sampled blurred content that was produced by the generate() method
+ * @param input original unblurred input that is used to crossfade with the blurredImage
+ */
+ void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius,
+ const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+ sk_sp<SkImage> input);
private:
sk_sp<SkRuntimeEffect> mBlurEffect;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 85322a9..72e32ed 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -263,6 +263,11 @@
}
}
+ void expectBufferColor(const Point& point, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ uint8_t tolerance = 0) {
+ expectBufferColor(Rect(point.x, point.y, point.x + 1, point.y + 1), r, g, b, a, tolerance);
+ }
+
void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
uint8_t tolerance = 0) {
auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
@@ -1887,6 +1892,51 @@
0, 255, 0, 255);
}
+TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<const renderengine::LayerSettings*> layers;
+
+ renderengine::LayerSettings redLayer;
+ redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ redLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ redLayer.geometry.roundedCornersRadius = 5.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);
+
+ // Green layer with 1/2 size with parent crop rect.
+ renderengine::LayerSettings greenLayer = redLayer;
+ greenLayer.geometry.boundaries =
+ FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2);
+ greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
+
+ layers.push_back(&greenLayer);
+
+ 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);
+
+ // top middle should be green and the bottom middle red
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 0), 0, 255, 0, 255);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255);
+
+ // the bottom edge of the green layer should not be rounded
+ expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255);
+}
+
TEST_P(RenderEngineTest, testClear) {
initializeRenderEngine();