diff options
40 files changed, 1116 insertions, 132 deletions
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto index bbf4f8dd38..c6f656ba0c 100644 --- a/cmds/surfacereplayer/proto/src/trace.proto +++ b/cmds/surfacereplayer/proto/src/trace.proto @@ -54,6 +54,7 @@ message SurfaceChange { ReparentChildrenChange reparent_children = 20; BackgroundBlurRadiusChange background_blur_radius = 21; ShadowRadiusChange shadow_radius = 22; + BlurRegionsChange blur_regions = 23; } } @@ -207,6 +208,23 @@ message ShadowRadiusChange { required float radius = 1; } +message BlurRegionsChange { + repeated BlurRegionChange blur_regions = 1; +} + +message BlurRegionChange { + required uint32 blur_radius = 1; + required float corner_radius_tl = 2; + required float corner_radius_tr = 3; + required float corner_radius_bl = 4; + required float corner_radius_br = 5; + required float alpha = 6; + required int32 left = 7; + required int32 top = 8; + required int32 right = 9; + required int32 bottom = 10; +} + message Origin { required int32 pid = 1; required int32 uid = 2; diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index c1868016d0..5849212265 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -423,6 +423,9 @@ status_t Replayer::doSurfaceTransaction( case SurfaceChange::SurfaceChangeCase::kShadowRadius: setShadowRadiusChange(transaction, change.id(), change.shadow_radius()); break; + case SurfaceChange::SurfaceChangeCase::kBlurRegions: + setBlurRegionsChange(transaction, change.id(), change.blur_regions()); + break; default: status = 1; break; @@ -728,3 +731,24 @@ void Replayer::setShadowRadiusChange(SurfaceComposerClient::Transaction& t, layer_id id, const ShadowRadiusChange& c) { t.setShadowRadius(mLayers[id], c.radius()); } + +void Replayer::setBlurRegionsChange(SurfaceComposerClient::Transaction& t, + layer_id id, const BlurRegionsChange& c) { + std::vector<BlurRegion> regions; + for(size_t i=0; i < c.blur_regions_size(); i++) { + auto protoRegion = c.blur_regions(i); + regions.push_back(BlurRegion{ + .blurRadius = protoRegion.blur_radius(), + .alpha = protoRegion.alpha(), + .cornerRadiusTL = protoRegion.corner_radius_tl(), + .cornerRadiusTR = protoRegion.corner_radius_tr(), + .cornerRadiusBL = protoRegion.corner_radius_bl(), + .cornerRadiusBR = protoRegion.corner_radius_br(), + .left = protoRegion.left(), + .top = protoRegion.top(), + .right = protoRegion.right(), + .bottom = protoRegion.bottom() + }); + } + t.setBlurRegions(mLayers[id], regions); +} diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h index e439718bd6..a22262a24e 100644 --- a/cmds/surfacereplayer/replayer/Replayer.h +++ b/cmds/surfacereplayer/replayer/Replayer.h @@ -96,6 +96,8 @@ class Replayer { layer_id id, const CornerRadiusChange& cc); void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t, layer_id id, const BackgroundBlurRadiusChange& cc); + void setBlurRegions(SurfaceComposerClient::Transaction& t, + layer_id id, const BlurRegionsChange& cc); void setMatrix(SurfaceComposerClient::Transaction& t, layer_id id, const MatrixChange& mc); void setTransparentRegionHint(SurfaceComposerClient::Transaction& t, @@ -120,6 +122,8 @@ class Replayer { layer_id id, const ReparentChildrenChange& c); void setShadowRadiusChange(SurfaceComposerClient::Transaction& t, layer_id id, const ShadowRadiusChange& c); + void setBlurRegionsChange(SurfaceComposerClient::Transaction& t, + layer_id id, const BlurRegionsChange& c); void setDisplaySurface(SurfaceComposerClient::Transaction& t, display_id id, const DispSurfaceChange& dsc); diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 433312639d..9722f368f4 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -147,6 +147,20 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeUint32, fixedTransformHint); SAFE_PARCEL(output.writeUint64, frameNumber); SAFE_PARCEL(output.writeInt64, frameTimelineVsyncId); + + SAFE_PARCEL(output.writeUint32, blurRegions.size()); + for (auto region : blurRegions) { + SAFE_PARCEL(output.writeUint32, region.blurRadius); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusTL); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusTR); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusBL); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusBR); + SAFE_PARCEL(output.writeFloat, region.alpha); + SAFE_PARCEL(output.writeInt32, region.left); + SAFE_PARCEL(output.writeInt32, region.top); + SAFE_PARCEL(output.writeInt32, region.right); + SAFE_PARCEL(output.writeInt32, region.bottom); + } return NO_ERROR; } @@ -252,6 +266,24 @@ status_t layer_state_t::read(const Parcel& input) fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32); SAFE_PARCEL(input.readUint64, &frameNumber); SAFE_PARCEL(input.readInt64, &frameTimelineVsyncId); + + uint32_t numRegions = 0; + SAFE_PARCEL(input.readUint32, &numRegions); + blurRegions.clear(); + for (uint32_t i = 0; i < numRegions; i++) { + BlurRegion region; + SAFE_PARCEL(input.readUint32, ®ion.blurRadius); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusTL); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusTR); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusBL); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusBR); + SAFE_PARCEL(input.readFloat, ®ion.alpha); + SAFE_PARCEL(input.readInt32, ®ion.left); + SAFE_PARCEL(input.readInt32, ®ion.top); + SAFE_PARCEL(input.readInt32, ®ion.right); + SAFE_PARCEL(input.readInt32, ®ion.bottom); + blurRegions.push_back(region); + } return NO_ERROR; } @@ -375,6 +407,10 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eBackgroundBlurRadiusChanged; backgroundBlurRadius = other.backgroundBlurRadius; } + if (other.what & eBlurRegionsChanged) { + what |= eBlurRegionsChanged; + blurRegions = other.blurRegions; + } if (other.what & eDeferTransaction_legacy) { what |= eDeferTransaction_legacy; barrierSurfaceControl_legacy = other.barrierSurfaceControl_legacy; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 32f9695b8c..039e9008e8 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1017,6 +1017,18 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackg return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBlurRegions( + const sp<SurfaceControl>& sc, const std::vector<BlurRegion>& blurRegions) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eBlurRegionsChanged; + s->blurRegions = blurRegions; + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil_legacy( const sp<SurfaceControl>& sc, const sp<SurfaceControl>& barrierSurfaceControl, diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index ff3a87d98f..a73d9a68a5 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -51,6 +51,7 @@ #include <gui/LayerMetadata.h> #include <gui/SurfaceControl.h> #include <math/vec3.h> +#include <ui/BlurRegion.h> #include <ui/GraphicTypes.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -128,6 +129,7 @@ struct layer_state_t { eFixedTransformHintChanged = 0x200'00000000, eFrameNumberChanged = 0x400'00000000, eFrameTimelineVsyncChanged = 0x800'00000000, + eBlurRegionsChanged = 0x1000'00000000, }; layer_state_t(); @@ -186,6 +188,7 @@ struct layer_state_t { int32_t api; sp<NativeHandle> sidebandStream; mat4 colorTransform; + std::vector<BlurRegion> blurRegions; #ifndef NO_INPUT sp<InputWindowHandle> inputHandle = new InputWindowHandle(); diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 13abed2713..73909a30bb 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -29,6 +29,7 @@ #include <utils/SortedVector.h> #include <utils/threads.h> +#include <ui/BlurRegion.h> #include <ui/ConfigStoreTypes.h> #include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> @@ -439,6 +440,8 @@ public: Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius); Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc, int backgroundBlurRadius); + Transaction& setBlurRegions(const sp<SurfaceControl>& sc, + const std::vector<BlurRegion>& regions); Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack); Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p); // Defers applying any changes made in this transaction until the Layer diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index 95e9367fea..d8d989e95d 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -21,6 +21,7 @@ #include <math/mat4.h> #include <math/vec3.h> #include <renderengine/Texture.h> +#include <ui/BlurRegion.h> #include <ui/Fence.h> #include <ui/FloatRect.h> #include <ui/GraphicBuffer.h> @@ -151,6 +152,8 @@ struct LayerSettings { ShadowSettings shadow; int backgroundBlurRadius = 0; + + std::vector<BlurRegion> blurRegions; }; // Keep in sync with custom comparison function in @@ -182,7 +185,29 @@ static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& r lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent; } +static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) { + return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL && + lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL && + lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius && + lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right && + lhs.bottom == rhs.bottom; +} + +static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) { + return !(lhs == rhs); +} + static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) { + if (lhs.blurRegions.size() != rhs.blurRegions.size()) { + return false; + } + const auto size = lhs.blurRegions.size(); + for (size_t i = 0; i < size; i++) { + if (lhs.blurRegions[i] != rhs.blurRegions[i]) { + return false; + } + } + return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha && lhs.sourceDataspace == rhs.sourceDataspace && lhs.colorTransform == rhs.colorTransform && diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 69ad189765..03c8e80d04 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -23,6 +23,14 @@ #include <EGL/egl.h> #include <EGL/eglext.h> #include <GLES2/gl2.h> +#include <sync/sync.h> +#include <ui/BlurRegion.h> +#include <ui/GraphicBuffer.h> +#include <utils/Trace.h> +#include "../gl/GLExtensions.h" +#include "SkiaGLRenderEngine.h" +#include "filters/BlurFilter.h" + #include <GrContextOptions.h> #include <SkCanvas.h> #include <SkColorFilter.h> @@ -500,10 +508,25 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, SkPaint paint; const auto& bounds = layer->geometry.boundaries; const auto dest = getSkRect(bounds); - - if (layer->backgroundBlurRadius > 0) { - ATRACE_NAME("BackgroundBlur"); - mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius); + std::unordered_map<uint32_t, sk_sp<SkSurface>> cachedBlurs; + + if (mBlurFilter) { + if (layer->backgroundBlurRadius > 0) { + ATRACE_NAME("BackgroundBlur"); + auto blurredSurface = + mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius); + cachedBlurs[layer->backgroundBlurRadius] = blurredSurface; + } + if (layer->blurRegions.size() > 0) { + for (auto region : layer->blurRegions) { + if (cachedBlurs[region.blurRadius]) { + continue; + } + ATRACE_NAME("BlurRegion"); + auto blurredSurface = mBlurFilter->generate(canvas, surface, region.blurRadius); + cachedBlurs[region.blurRadius] = blurredSurface; + } + } } if (layer->source.buffer.buffer) { @@ -571,7 +594,11 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, } else { ATRACE_NAME("DrawColor"); const auto color = layer->source.solidColor; - paint.setColor(SkColor4f{.fR = color.r, .fG = color.g, .fB = color.b, layer->alpha}); + paint.setShader(SkShaders::Color(SkColor4f{.fR = color.r, + .fG = color.g, + .fB = color.b, + layer->alpha}, + nullptr)); } paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform))); @@ -580,6 +607,10 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, canvas->save(); canvas->concat(getSkM44(layer->geometry.positionTransform)); + for (const auto effectRegion : layer->blurRegions) { + drawBlurRegion(canvas, effectRegion, dest, cachedBlurs[effectRegion.blurRadius]); + } + if (layer->shadow.length > 0) { const auto rect = layer->geometry.roundedCornersRadius > 0 ? getSkRect(layer->geometry.roundedCornersCrop) @@ -592,6 +623,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, } else { canvas->drawRect(dest, paint); } + canvas->restore(); } { @@ -678,6 +710,34 @@ void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, flags); } +void SkiaGLRenderEngine::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion, + const SkRect& layerBoundaries, + sk_sp<SkSurface> blurredSurface) { + ATRACE_CALL(); + SkPaint paint; + paint.setAlpha(static_cast<int>(effectRegion.alpha * 255)); + const auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right, + effectRegion.bottom); + + const auto matrix = mBlurFilter->getShaderMatrix( + SkMatrix::MakeTrans(layerBoundaries.left(), layerBoundaries.top())); + paint.setShader(blurredSurface->makeImageSnapshot()->makeShader(matrix)); + + 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); + } else { + canvas->drawRect(rect, paint); + } +} + EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, bool useContextPriority, Protection protection) { diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index ed4ba111e8..0143445251 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -75,6 +75,8 @@ private: bool waitFence(base::unique_fd fenceFd); void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius, const ShadowSettings& shadowSettings); + void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerBounds, + sk_sp<SkSurface> blurrendSurface); EGLDisplay mEGLDisplay; EGLConfig mEGLConfig; diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp index 8704b3ad68..f6a316fa1d 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -55,8 +55,10 @@ BlurFilter::BlurFilter() { mBlurEffect = std::move(blurEffect); } -void BlurFilter::draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t blurRadius) const { +sk_sp<SkSurface> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input, + const uint32_t blurRadius) const { ATRACE_CALL(); + // Kawase is an approximation of Gaussian, but it behaves differently from it. // A radius transformation is required for approximating them, and also to introduce // non-integer steps, necessary to smoothly interpolate large radii. @@ -111,25 +113,35 @@ void BlurFilter::draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t b drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint); // Swap buffers for next iteration - auto tmp = drawSurface; + const auto tmp = drawSurface; drawSurface = readSurface; readSurface = tmp; blurBuilder.child("input") = nullptr; } lastDrawTarget = readSurface; } + lastDrawTarget->flushAndSubmit(); + return lastDrawTarget; +} - drawSurface->flushAndSubmit(); +sk_sp<SkSurface> BlurFilter::draw(SkCanvas* canvas, const sk_sp<SkSurface> input, + const uint32_t blurRadius) const { + ATRACE_CALL(); + auto surface = generate(canvas, input, blurRadius); + + SkPaint paint; + const auto image = surface->makeImageSnapshot(); + paint.setShader(image->makeShader(SkMatrix::MakeScale(kInverseInputScale))); + paint.setFilterQuality(kLow_SkFilterQuality); + paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255); + canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint); + return surface; +} - // do the final composition, with alpha blending to hide downscaling artifacts. - { - SkPaint paint; - paint.setShader(lastDrawTarget->makeImageSnapshot()->makeShader( - SkMatrix::MakeScale(kInverseInputScale))); - paint.setFilterQuality(kLow_SkFilterQuality); - paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255); - canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint); - } +SkMatrix BlurFilter::getShaderMatrix(const SkMatrix& transformMatrix) const { + SkMatrix matrix; + matrix.setConcat(transformMatrix, SkMatrix::MakeScale(kInverseInputScale)); + return matrix; } } // namespace skia diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h index 94b3673fdc..6f973d790e 100644 --- a/libs/renderengine/skia/filters/BlurFilter.h +++ b/libs/renderengine/skia/filters/BlurFilter.h @@ -47,8 +47,14 @@ public: explicit BlurFilter(); virtual ~BlurFilter(){}; - // Execute blur passes, rendering to a canvas. - void draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t radius) const; + // Execute blur, saving it to a texture + sk_sp<SkSurface> generate(SkCanvas* canvas, const sk_sp<SkSurface> input, + const uint32_t radius) const; + // Same as generate but also drawing to the screen + sk_sp<SkSurface> draw(SkCanvas* canvas, const sk_sp<SkSurface> input, + const uint32_t radius) const; + // Returns a matrix that should be applied to the blur shader + SkMatrix getShaderMatrix(const SkMatrix& transformMatrix) const; private: sk_sp<SkRuntimeEffect> mBlurEffect; diff --git a/libs/ui/include/ui/BlurRegion.h b/libs/ui/include/ui/BlurRegion.h new file mode 100644 index 0000000000..c5a5d477cf --- /dev/null +++ b/libs/ui/include/ui/BlurRegion.h @@ -0,0 +1,36 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <inttypes.h> + +namespace android { + +struct BlurRegion { + uint32_t blurRadius; + float cornerRadiusTL; + float cornerRadiusTR; + float cornerRadiusBL; + float cornerRadiusBR; + float alpha; + int left; + int top; + int right; + int bottom; +}; + +} // namespace android
\ No newline at end of file diff --git a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp index afde5f7f32..f5c5a5a4c4 100644 --- a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp +++ b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp @@ -52,7 +52,7 @@ struct FuzzType { // Fuzzer for Serialization operations, this is mostly just lifted from the // existing test cases to use fuzzed values as inputs. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +void FuzzSerializeDeserialize(const uint8_t* data, size_t size) { FuzzedDataProvider fdp = FuzzedDataProvider(data, size); Payload result; @@ -106,6 +106,183 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { Deserialize(&vec_val, &result); Serialize(t1_val, &result); Deserialize(&t1_val, &result); +} + +void FuzzDeserializeUint8(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = {ENCODING_TYPE_UINT8, fdp.ConsumeIntegral<uint8_t>()}; + std::uint8_t result; + Deserialize(&result, &buffer); +} + +void FuzzDeserializeUint16(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = {ENCODING_TYPE_UINT16, fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>()}; + std::uint16_t result; + Deserialize(&result, &buffer); +} + +void FuzzDeserializeUint32(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = {ENCODING_TYPE_UINT32, fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>()}; + std::uint32_t result; + Deserialize(&result, &buffer); +} + +void FuzzDeserializeUint64(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = { + ENCODING_TYPE_UINT64, fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>()}; + std::uint64_t result; + Deserialize(&result, &buffer); +} + +void FuzzDeserializeInt8(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = {ENCODING_TYPE_INT8, fdp.ConsumeIntegral<uint8_t>()}; + std::int8_t result; + Deserialize(&result, &buffer); +} + +void FuzzDeserializeInt16(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = {ENCODING_TYPE_INT16, fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>()}; + std::int16_t result; + Deserialize(&result, &buffer); +} + +void FuzzDeserializeInt32(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = {ENCODING_TYPE_INT32, fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>()}; + std::int32_t result; + Deserialize(&result, &buffer); +} + +void FuzzDeserializeInt64(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = {ENCODING_TYPE_INT64, + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>()}; + std::int64_t result; + Deserialize(&result, &buffer); +} + +void FuzzDeserializeFloat32(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = {ENCODING_TYPE_FLOAT32, fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>()}; + float floatResult; + Deserialize(&floatResult, &buffer); + + buffer.Rewind(); + double doubleResult; + Deserialize(&doubleResult, &buffer); +} + +void FuzzDeserializeFloat64(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = { + ENCODING_TYPE_FLOAT64, fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>()}; + double result; + Deserialize(&result, &buffer); +} + +void FuzzDeserializeFixstr(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + std::string s_val = fdp.ConsumeRemainingBytesAsString(); + Payload buffer = {ENCODING_TYPE_FIXSTR_MAX}; + for (std::string::iterator iter = s_val.begin(); iter != s_val.end(); + iter++) { + buffer.Append(1, *iter); + } + std::string result; + Deserialize(&result, &buffer); +} + +void FuzzDeserializeFixmap(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = {ENCODING_TYPE_FIXMAP_MAX}; + // Fill the map with the fuzzed data, not attempting to + // make a valid map + while (fdp.remaining_bytes() > 0) { + buffer.Append(1, fdp.ConsumeIntegral<uint8_t>()); + } + + std::map<std::uint32_t, std::uint32_t> result; + Deserialize(&result, &buffer); + + buffer.Rewind(); + std::unordered_map<std::uint32_t, std::uint32_t> unorderedResult; + Deserialize(&unorderedResult, &buffer); +} + +void FuzzDeserializeVariant(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload buffer = {ENCODING_TYPE_INT16, + ENCODING_TYPE_FLOAT32, + ENCODING_TYPE_FIXSTR_MAX, + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>(), + fdp.ConsumeIntegral<uint8_t>()}; + // Add the rest of the data as a string + std::string s_val = fdp.ConsumeRemainingBytesAsString(); + for (std::string::iterator iter = s_val.begin(); iter != s_val.end(); + iter++) { + buffer.Append(1, *iter); + } + Variant<int, float, std::string> result; + Deserialize(&result, &buffer); +} + +// Attempts to deserialize fuzzed data as various types +void FuzzDeserialize(const uint8_t* data, size_t size) { + FuzzDeserializeUint8(data, size); + FuzzDeserializeUint16(data, size); + FuzzDeserializeUint32(data, size); + FuzzDeserializeUint64(data, size); + FuzzDeserializeInt8(data, size); + FuzzDeserializeInt16(data, size); + FuzzDeserializeInt32(data, size); + FuzzDeserializeInt64(data, size); + FuzzDeserializeFloat32(data, size); + FuzzDeserializeFloat64(data, size); + FuzzDeserializeFixstr(data, size); + FuzzDeserializeFixmap(data, size); + FuzzDeserializeVariant(data, size); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzSerializeDeserialize(data, size); + FuzzDeserialize(data, size); return 0; } diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 9c3fdbb405..d302f987b1 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -204,8 +204,8 @@ std::optional<compositionengine::LayerFE::LayerSettings> BufferLayer::prepareCli layer.frameNumber = mCurrentFrameNumber; layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0; - // TODO: we could be more subtle with isFixedSize() - const bool useFiltering = targetSettings.needsFiltering || mNeedsFiltering || isFixedSize(); + const bool useFiltering = + targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering(); // Query the texture matrix given our current filtering mode. float textureMatrix[16]; @@ -822,6 +822,10 @@ void BufferLayer::setTransformHint(ui::Transform::RotationFlags displayTransform } } +bool BufferLayer::bufferNeedsFiltering() const { + return isFixedSize(); +} + } // namespace android #if defined(__gl_h_) diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index e6a0f81622..deaf8461e8 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -115,6 +115,10 @@ public: ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; } + // Returns true if the transformed buffer size does not match the layer size and we need + // to apply filtering. + virtual bool bufferNeedsFiltering() const; + protected: struct BufferInfo { nsecs_t mDesiredPresentTime; diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index ec828d435b..9ab2974801 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -442,7 +442,8 @@ void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { } auto surfaceFrame = - mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId); + mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerUid, mName, mName, + mFrameTimelineVsyncId); surfaceFrame->setActualQueueTime(systemTime()); mQueueItems.push_back({item, std::move(surfaceFrame)}); @@ -480,7 +481,8 @@ void BufferQueueLayer::onFrameReplaced(const BufferItem& item) { } auto surfaceFrame = - mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId); + mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerUid, mName, mName, + mFrameTimelineVsyncId); surfaceFrame->setActualQueueTime(systemTime()); mQueueItems[mQueueItems.size() - 1].item = item; mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame); diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 6f344c721a..a64b2434d3 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -277,7 +277,7 @@ bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence const int32_t layerId = getSequence(); mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(), - postTime); + mOwnerUid, postTime); desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime; mCurrentState.desiredPresentTime = desiredPresentTime; @@ -744,6 +744,31 @@ Layer::RoundedCornerState BufferStateLayer::getRoundedCornerState() const { static_cast<float>(s.active.transform.ty() + s.active.h)), radius); } + +bool BufferStateLayer::bufferNeedsFiltering() const { + const State& s(getDrawingState()); + if (!s.buffer) { + return false; + } + + uint32_t bufferWidth = s.buffer->width; + uint32_t bufferHeight = s.buffer->height; + + // Undo any transformations on the buffer and return the result. + if (s.transform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + + if (s.transformToDisplayInverse) { + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + } + + const Rect layerSize{getBounds()}; + return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight; +} } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 4773286e44..104a13be71 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -143,6 +143,8 @@ private: bool willPresentCurrentTransaction() const; + bool bufferNeedsFiltering() const override; + static const std::array<float, 16> IDENTITY_MATRIX; std::unique_ptr<renderengine::Image> mTextureImage; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index 77400eb42b..5a3b9ac817 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -20,6 +20,7 @@ #include <gui/HdrMetadata.h> #include <math/mat4.h> +#include <ui/BlurRegion.h> #include <ui/FloatRect.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -118,6 +119,9 @@ struct LayerFECompositionState { // length of the shadow in screen space float shadowRadius{0.f}; + // List of regions that require blur + std::vector<BlurRegion> blurRegions; + /* * Geometry state */ diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index c625b2e81b..3852f45e35 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -669,7 +669,8 @@ void Output::updateAndWriteCompositionState( compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const { compositionengine::OutputLayer* layerRequestingBgComposition = nullptr; for (auto* layer : getOutputLayersOrderedByZ()) { - if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0) { + if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0 || + layer->getLayerFE().getCompositionState()->blurRegions.size() > 0) { layerRequestingBgComposition = layer; } } diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 3dd18396c3..9badb99f96 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -4014,6 +4014,34 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) mOutput->updateAndWriteCompositionState(args); } +TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) { + InjectedLayer layer1; + InjectedLayer layer2; + InjectedLayer layer3; + + // Layer requesting blur, or below, should request client composition. + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); + EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); + EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false)); + + BlurRegion region; + layer2.layerFEState.blurRegions.push_back(region); + + injectOutputLayer(layer1); + injectOutputLayer(layer2); + injectOutputLayer(layer3); + + mOutput->editState().isEnabled = true; + + CompositionRefreshArgs args; + args.updatingGeometryThisFrame = false; + args.devOptForceClientComposition = false; + mOutput->updateAndWriteCompositionState(args); +} + TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) { // In split-screen landscape mode, the screen is rotated 90 degrees, with // one layer on the left covering the left side of the output, and one layer diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp index 6ba4c432c6..e075d3e1a0 100644 --- a/services/surfaceflinger/FrameTimeline/Android.bp +++ b/services/surfaceflinger/FrameTimeline/Android.bp @@ -5,10 +5,12 @@ cc_library_static { "FrameTimeline.cpp", ], shared_libs: [ + "android.hardware.graphics.composer@2.4", "libbase", "libcutils", "liblog", "libgui", + "libtimestats", "libui", "libutils", ], diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 43176a3f10..996479c1c7 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -93,19 +93,19 @@ std::string toString(PredictionState predictionState) { } } -std::string toString(JankType jankType) { +std::string toString(TimeStats::JankType jankType) { switch (jankType) { - case JankType::None: + case TimeStats::JankType::None: return "None"; - case JankType::Display: + case TimeStats::JankType::Display: return "Composer/Display - outside SF and App"; - case JankType::SurfaceFlingerDeadlineMissed: + case TimeStats::JankType::SurfaceFlingerDeadlineMissed: return "SurfaceFlinger Deadline Missed"; - case JankType::AppDeadlineMissed: + case TimeStats::JankType::AppDeadlineMissed: return "App Deadline Missed"; - case JankType::PredictionExpired: + case TimeStats::JankType::PredictionExpired: return "Prediction Expired"; - case JankType::SurfaceFlingerEarlyLatch: + case TimeStats::JankType::SurfaceFlingerEarlyLatch: return "SurfaceFlinger Early Latch"; default: return "Unclassified"; @@ -177,15 +177,18 @@ void TokenManager::flushTokens(nsecs_t flushTime) { } } -SurfaceFrame::SurfaceFrame(const std::string& layerName, PredictionState predictionState, +SurfaceFrame::SurfaceFrame(uid_t ownerUid, std::string layerName, std::string debugName, + PredictionState predictionState, frametimeline::TimelineItem&& predictions) - : mLayerName(layerName), + : mOwnerUid(ownerUid), + mLayerName(std::move(layerName)), + mDebugName(std::move(debugName)), mPresentState(PresentState::Unknown), mPredictionState(predictionState), mPredictions(predictions), mActuals({0, 0, 0}), mActualQueueTime(0), - mJankType(JankType::None), + mJankType(TimeStats::JankType::None), mJankMetadata(0) {} void SurfaceFrame::setPresentState(PresentState state) { @@ -227,17 +230,25 @@ void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) { mActuals.presentTime = presentTime; } -void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) { +void SurfaceFrame::setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata) { std::lock_guard<std::mutex> lock(mMutex); mJankType = jankType; mJankMetadata = jankMetadata; } -JankType SurfaceFrame::getJankType() const { +TimeStats::JankType SurfaceFrame::getJankType() const { std::lock_guard<std::mutex> lock(mMutex); return mJankType; } +uid_t SurfaceFrame::getOwnerUid() const { + return mOwnerUid; +} + +const std::string& SurfaceFrame::getName() const { + return mLayerName; +} + nsecs_t SurfaceFrame::getBaseTime() const { std::lock_guard<std::mutex> lock(mMutex); nsecs_t baseTime = std::numeric_limits<nsecs_t>::max(); @@ -267,8 +278,8 @@ std::string presentStateToString(SurfaceFrame::PresentState presentState) { void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) { std::lock_guard<std::mutex> lock(mMutex); StringAppendF(&result, "%s", indent.c_str()); - StringAppendF(&result, "Layer - %s", mLayerName.c_str()); - if (mJankType != JankType::None) { + StringAppendF(&result, "Layer - %s", mDebugName.c_str()); + if (mJankType != TimeStats::JankType::None) { // Easily identify a janky Surface Frame in the dump StringAppendF(&result, " [*] "); } @@ -285,33 +296,35 @@ void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime); } -FrameTimeline::FrameTimeline() +FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats) : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()), - mMaxDisplayFrames(kDefaultMaxDisplayFrames) {} + mMaxDisplayFrames(kDefaultMaxDisplayFrames), + mTimeStats(std::move(timeStats)) {} FrameTimeline::DisplayFrame::DisplayFrame() : surfaceFlingerPredictions(TimelineItem()), surfaceFlingerActuals(TimelineItem()), predictionState(PredictionState::None), - jankType(JankType::None), + jankType(TimeStats::JankType::None), jankMetadata(0) { this->surfaceFrames.reserve(kNumSurfaceFramesInitial); } std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken( - const std::string& layerName, std::optional<int64_t> token) { + uid_t uid, std::string layerName, std::string debugName, std::optional<int64_t> token) { ATRACE_CALL(); if (!token) { - return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::None, - TimelineItem()); + return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName), + PredictionState::None, TimelineItem()); } std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token); if (predictions) { - return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Valid, + return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName), + PredictionState::Valid, std::move(*predictions)); } - return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Expired, - TimelineItem()); + return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName), + PredictionState::Expired, TimelineItem()); } void FrameTimeline::addSurfaceFrame( @@ -359,6 +372,7 @@ void FrameTimeline::flushPendingPresentFences() { } } if (signalTime != Fence::SIGNAL_TIME_INVALID) { + int32_t totalJankReasons = TimeStats::JankType::None; auto& displayFrame = pendingPresentFence.second; displayFrame->surfaceFlingerActuals.presentTime = signalTime; @@ -377,21 +391,26 @@ void FrameTimeline::flushPendingPresentFences() { displayFrame->jankMetadata |= EarlyFinish; } - if (displayFrame->jankMetadata & EarlyFinish & EarlyPresent) { - displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch; - } else if (displayFrame->jankMetadata & LateFinish & LatePresent) { - displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed; + if ((displayFrame->jankMetadata & EarlyFinish) && + (displayFrame->jankMetadata & EarlyPresent)) { + displayFrame->jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch; + } else if ((displayFrame->jankMetadata & LateFinish) && + (displayFrame->jankMetadata & LatePresent)) { + displayFrame->jankType = TimeStats::JankType::SurfaceFlingerDeadlineMissed; } else if (displayFrame->jankMetadata & EarlyPresent || displayFrame->jankMetadata & LatePresent) { // Cases where SF finished early but frame was presented late and vice versa - displayFrame->jankType = JankType::Display; + displayFrame->jankType = TimeStats::JankType::Display; } } + if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) { displayFrame->jankMetadata |= sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart; } + totalJankReasons |= displayFrame->jankType; + for (auto& surfaceFrame : displayFrame->surfaceFrames) { if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) { // Only presented SurfaceFrames need to be updated @@ -401,13 +420,13 @@ void FrameTimeline::flushPendingPresentFences() { const auto& predictionState = surfaceFrame->getPredictionState(); if (predictionState == PredictionState::Expired) { // Jank analysis cannot be done on apps that don't use predictions - surfaceFrame->setJankInfo(JankType::PredictionExpired, 0); + surfaceFrame->setJankInfo(TimeStats::JankType::PredictionExpired, 0); continue; } else if (predictionState == PredictionState::Valid) { const auto& actuals = surfaceFrame->getActuals(); const auto& predictions = surfaceFrame->getPredictions(); int32_t jankMetadata = 0; - JankType jankType = JankType::None; + TimeStats::JankType jankType = TimeStats::JankType::None; if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) { jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish : EarlyFinish; @@ -419,19 +438,26 @@ void FrameTimeline::flushPendingPresentFences() { : EarlyPresent; } if (jankMetadata & EarlyPresent) { - jankType = JankType::SurfaceFlingerEarlyLatch; + jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch; } else if (jankMetadata & LatePresent) { if (jankMetadata & EarlyFinish) { // TODO(b/169890654): Classify this properly - jankType = JankType::Display; + jankType = TimeStats::JankType::Display; } else { - jankType = JankType::AppDeadlineMissed; + jankType = TimeStats::JankType::AppDeadlineMissed; } } + + totalJankReasons |= jankType; + mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(), + surfaceFrame->getName(), + jankType | displayFrame->jankType); surfaceFrame->setJankInfo(jankType, jankMetadata); } } } + + mTimeStats->incrementJankyFrames(totalJankReasons); } mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); @@ -467,7 +493,7 @@ nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& display void FrameTimeline::dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>& displayFrame, nsecs_t baseTime) { - if (displayFrame->jankType != JankType::None) { + if (displayFrame->jankType != TimeStats::JankType::None) { // Easily identify a janky Display Frame in the dump StringAppendF(&result, " [*] "); } @@ -501,11 +527,11 @@ void FrameTimeline::dumpJank(std::string& result) { nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]); for (size_t i = 0; i < mDisplayFrames.size(); i++) { const auto& displayFrame = mDisplayFrames[i]; - if (displayFrame->jankType == JankType::None) { + if (displayFrame->jankType == TimeStats::JankType::None) { // Check if any Surface Frame has been janky bool isJanky = false; for (const auto& surfaceFrame : displayFrame->surfaceFrames) { - if (surfaceFrame->getJankType() != JankType::None) { + if (surfaceFrame->getJankType() != TimeStats::JankType::None) { isJanky = true; break; } diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index bd637df8bc..33821e51c3 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -16,9 +16,7 @@ #pragma once -#include <deque> -#include <mutex> - +#include <../TimeStats/TimeStats.h> #include <gui/ISurfaceComposer.h> #include <ui/FenceTime.h> #include <utils/RefBase.h> @@ -26,26 +24,10 @@ #include <utils/Timers.h> #include <utils/Vector.h> -namespace android::frametimeline { +#include <deque> +#include <mutex> -/* - * The type of jank that is associated with a Display/Surface frame - */ -enum class JankType { - // No Jank - None, - // Jank not related to SurfaceFlinger or the App - Display, - // SF took too long on the CPU - SurfaceFlingerDeadlineMissed, - // Either App or GPU took too long on the frame - AppDeadlineMissed, - // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a jank - // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame. - PredictionExpired, - // Latching a buffer early might cause an early present of the frame - SurfaceFlingerEarlyLatch, -}; +namespace android::frametimeline { enum JankMetadata { // Frame was presented earlier than expected @@ -147,8 +129,10 @@ public: // Create a new surface frame, set the predictions based on a token and return it to the caller. // Sets the PredictionState of SurfaceFrame. + // Debug name is the human-readable debugging string for dumpsys. virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken( - const std::string& layerName, std::optional<int64_t> token) = 0; + uid_t uid, std::string layerName, std::string debugName, + std::optional<int64_t> token) = 0; // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be // composited into one display frame. @@ -206,8 +190,8 @@ private: class SurfaceFrame : public android::frametimeline::SurfaceFrame { public: - SurfaceFrame(const std::string& layerName, PredictionState predictionState, - TimelineItem&& predictions); + SurfaceFrame(uid_t uid, std::string layerName, std::string debugName, + PredictionState predictionState, TimelineItem&& predictions); ~SurfaceFrame() = default; TimelineItem getPredictions() const override { return mPredictions; }; @@ -221,32 +205,37 @@ public: void setAcquireFenceTime(nsecs_t acquireFenceTime) override; void setPresentState(PresentState state) override; void setActualPresentTime(nsecs_t presentTime); - void setJankInfo(JankType jankType, int32_t jankMetadata); - JankType getJankType() const; + void setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata); + TimeStats::JankType getJankType() const; nsecs_t getBaseTime() const; + uid_t getOwnerUid() const; + const std::string& getName() const; // All the timestamps are dumped relative to the baseTime void dump(std::string& result, const std::string& indent, nsecs_t baseTime); private: + const uid_t mOwnerUid; const std::string mLayerName; + const std::string mDebugName; PresentState mPresentState GUARDED_BY(mMutex); const PredictionState mPredictionState; const TimelineItem mPredictions; TimelineItem mActuals GUARDED_BY(mMutex); nsecs_t mActualQueueTime GUARDED_BY(mMutex); mutable std::mutex mMutex; - JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank + TimeStats::JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank }; class FrameTimeline : public android::frametimeline::FrameTimeline { public: - FrameTimeline(); + FrameTimeline(std::shared_ptr<TimeStats> timeStats); ~FrameTimeline() = default; frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; } std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken( - const std::string& layerName, std::optional<int64_t> token) override; + uid_t ownerUid, std::string layerName, std::string debugName, + std::optional<int64_t> token) override; void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame, SurfaceFrame::PresentState state) override; void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override; @@ -278,7 +267,7 @@ private: std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames; PredictionState predictionState; - JankType jankType = JankType::None; // Enum for the type of jank + TimeStats::JankType jankType = TimeStats::JankType::None; // Enum for the type of jank int32_t jankMetadata = 0x0; // Additional details about the jank }; @@ -300,6 +289,7 @@ private: TokenManager mTokenManager; std::mutex mMutex; uint32_t mMaxDisplayFrames; + std::shared_ptr<TimeStats> mTimeStats; static constexpr uint32_t kDefaultMaxDisplayFrames = 64; // The initial container size for the vector<SurfaceFrames> inside display frame. Although this // number doesn't represent any bounds on the number of surface frames that can go in a display diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index f657a00065..ffae56fb26 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -482,6 +482,7 @@ void Layer::prepareBasicGeometryCompositionState() { compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); compositionState->alpha = alpha; compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius; + compositionState->blurRegions = drawingState.blurRegions; } void Layer::prepareGeometryCompositionState() { @@ -550,7 +551,8 @@ void Layer::preparePerFrameCompositionState() { isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf; // Force client composition for special cases known only to the front-end. - if (isHdrY410() || usesRoundedCorners || drawShadows()) { + if (isHdrY410() || usesRoundedCorners || drawShadows() || + getDrawingState().blurRegions.size() > 0) { compositionState->forceClientComposition = true; } } @@ -646,6 +648,7 @@ std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCom layerSettings.alpha = alpha; layerSettings.sourceDataspace = getDataSpace(); layerSettings.backgroundBlurRadius = getBackgroundBlurRadius(); + layerSettings.blurRegions = getBlurRegions(); return layerSettings; } @@ -897,7 +900,8 @@ bool Layer::applyPendingStates(State* stateToCommit) { : std::make_optional(stateToCommit->frameTimelineVsyncId); auto surfaceFrame = - mFlinger->mFrameTimeline->createSurfaceFrameForToken(mTransactionName, vsyncId); + mFlinger->mFrameTimeline->createSurfaceFrameForToken(getOwnerUid(), mName, + mTransactionName, vsyncId); surfaceFrame->setActualQueueTime(stateToCommit->postTime); // For transactions we set the acquire fence time to the post time as we // don't have a buffer. For BufferStateLayer it is overridden in @@ -1285,6 +1289,14 @@ bool Layer::setTransparentRegionHint(const Region& transparent) { return true; } +bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) { + mCurrentState.sequence++; + mCurrentState.blurRegions = blurRegions; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + bool Layer::setFlags(uint8_t flags, uint8_t mask) { const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask); if (mCurrentState.flags == newFlags) return false; @@ -1728,7 +1740,7 @@ void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) { if (newTimestamps) { mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber, - getName().c_str(), newTimestamps->postedTime); + getName().c_str(), mOwnerUid, newTimestamps->postedTime); mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber, newTimestamps->acquireFence); } @@ -2172,6 +2184,10 @@ int32_t Layer::getBackgroundBlurRadius() const { return getDrawingState().backgroundBlurRadius; } +const std::vector<BlurRegion>& Layer::getBlurRegions() const { + return getDrawingState().blurRegions; +} + Layer::RoundedCornerState Layer::getRoundedCornerState() const { const auto& p = mDrawingParent.promote(); if (p != nullptr) { diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 7112cbc435..5cb56a5a16 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -26,6 +26,7 @@ #include <renderengine/Mesh.h> #include <renderengine/Texture.h> #include <sys/types.h> +#include <ui/BlurRegion.h> #include <ui/FloatRect.h> #include <ui/FrameStats.h> #include <ui/GraphicBuffer.h> @@ -255,6 +256,9 @@ public: // be rendered around the layer. float shadowRadius; + // Layer regions that are made of custom materials, like frosted glass + std::vector<BlurRegion> blurRegions; + // Priority of the layer assigned by Window Manager. int32_t frameRateSelectionPriority; @@ -391,6 +395,7 @@ public: // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which // is specified in pixels. virtual bool setBackgroundBlurRadius(int backgroundBlurRadius); + virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions); virtual bool setTransparentRegionHint(const Region& transparent); virtual bool setFlags(uint8_t flags, uint8_t mask); virtual bool setLayerStack(uint32_t layerStack); @@ -1038,6 +1043,10 @@ protected: // Can only be accessed with the SF state lock held. std::unique_ptr<frametimeline::SurfaceFrame> mSurfaceFrame; + // The owner of the layer. If created from a non system process, it will be the calling uid. + // If created from a system process, the value can be passed in. + uid_t mOwnerUid; + private: virtual void setTransformHint(ui::Transform::RotationFlags) {} @@ -1096,10 +1105,6 @@ private: pid_t mCallingPid; uid_t mCallingUid; - // The owner of the layer. If created from a non system process, it will be the calling uid. - // If created from a system process, the value can be passed in. - uid_t mOwnerUid; - // The current layer is a clone of mClonedFrom. This means that this layer will update it's // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers, // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children, @@ -1110,6 +1115,9 @@ private: // final shadow radius for this layer. If a shadow is specified for a layer, then effective // shadow radius is the set shadow radius, otherwise its the parent's shadow radius. float mEffectiveShadowRadius = 0.f; + + // A list of regions on this layer that should have blurs. + const std::vector<BlurRegion>& getBlurRegions() const; }; } // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 3977e2b510..fde38c911e 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -331,7 +331,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) mInterceptor(mFactory.createSurfaceInterceptor()), mTimeStats(std::make_shared<impl::TimeStats>()), mFrameTracer(std::make_unique<FrameTracer>()), - mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>()), + mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>(mTimeStats)), mEventQueue(mFactory.createMessageQueue()), mCompositionEngine(mFactory.createCompositionEngine()), mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)), @@ -3684,6 +3684,9 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) { if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded; } + if (what & layer_state_t::eBlurRegionsChanged) { + if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded; + } if (what & layer_state_t::eLayerStackChanged) { ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); // We only allow setting layer stacks for top level layers, diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 2d5566f42a..354892386b 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -140,6 +140,7 @@ void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment, addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy); addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius); addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius); + addBlurRegionsLocked(transaction, layerId, layer->mCurrentState.blurRegions); if (layer->mCurrentState.barrierLayer_legacy != nullptr) { addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.barrierLayer_legacy.promote(), @@ -361,6 +362,25 @@ void SurfaceInterceptor::addBackgroundBlurRadiusLocked(Transaction* transaction, blurRadiusChange->set_background_blur_radius(backgroundBlurRadius); } +void SurfaceInterceptor::addBlurRegionsLocked(Transaction* transaction, int32_t layerId, + const std::vector<BlurRegion>& blurRegions) { + SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); + BlurRegionsChange* blurRegionsChange(change->mutable_blur_regions()); + for (const auto blurRegion : blurRegions) { + const auto blurRegionChange = blurRegionsChange->add_blur_regions(); + blurRegionChange->set_blur_radius(blurRegion.blurRadius); + blurRegionChange->set_corner_radius_tl(blurRegion.cornerRadiusTL); + blurRegionChange->set_corner_radius_tr(blurRegion.cornerRadiusTR); + blurRegionChange->set_corner_radius_bl(blurRegion.cornerRadiusBL); + blurRegionChange->set_corner_radius_br(blurRegion.cornerRadiusBR); + blurRegionChange->set_alpha(blurRegion.alpha); + blurRegionChange->set_left(blurRegion.left); + blurRegionChange->set_top(blurRegion.top); + blurRegionChange->set_right(blurRegion.right); + blurRegionChange->set_bottom(blurRegion.bottom); + } +} + void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId, const sp<const Layer>& layer, uint64_t frameNumber) { @@ -456,6 +476,9 @@ void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) { addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius); } + if (state.what & layer_state_t::eBlurRegionsChanged) { + addBlurRegionsLocked(transaction, layerId, state.blurRegions); + } if (state.what & layer_state_t::eDeferTransaction_legacy) { sp<Layer> otherLayer = nullptr; if (state.barrierSurfaceControl_legacy != nullptr) { diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h index 97ff547547..3df79c6eb5 100644 --- a/services/surfaceflinger/SurfaceInterceptor.h +++ b/services/surfaceflinger/SurfaceInterceptor.h @@ -165,6 +165,8 @@ private: void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius); void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId, int32_t backgroundBlurRadius); + void addBlurRegionsLocked(Transaction* transaction, int32_t layerId, + const std::vector<BlurRegion>& effectRegions); void addDeferTransactionLocked(Transaction* transaction, int32_t layerId, const sp<const Layer>& layer, uint64_t frameNumber); void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state); diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index 37194c6355..fe9e7378db 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" #undef LOG_TAG #define LOG_TAG "TimeStats" #define ATRACE_TAG ATRACE_TAG_GRAPHICS @@ -33,6 +30,8 @@ #include <algorithm> #include <chrono> +#include "timestatsproto/TimeStatsHelper.h" + namespace android { namespace impl { @@ -115,6 +114,13 @@ AStatsManager_PullAtomCallbackReturn TimeStats::populateGlobalAtom(AStatsEventLi mMaxPulledHistogramBuckets); mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)renderEngineTimingBytes.c_str(), renderEngineTimingBytes.size()); + + mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalFrames); + mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalJankyFrames); + mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongCpu); + mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongGpu); + mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFUnattributed); + mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalAppUnattributed); mStatsDelegate->statsEventBuild(event); clearGlobalLocked(); @@ -160,6 +166,13 @@ AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventLis mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames); mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames); + mStatsDelegate->statsEventWriteInt32(event, layer->uid); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalFrames); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalJankyFrames); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongCpu); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongGpu); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFUnattributed); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppUnattributed); mStatsDelegate->statsEventBuild(event); } @@ -397,11 +410,13 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) { timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime); if (prevTimeRecord.ready) { + uid_t uid = layerRecord.uid; const std::string& layerName = layerRecord.layerName; - if (!mTimeStats.stats.count(layerName)) { - mTimeStats.stats[layerName].layerName = layerName; + if (!mTimeStats.stats.count({uid, layerName})) { + mTimeStats.stats[{uid, layerName}].uid = uid; + mTimeStats.stats[{uid, layerName}].layerName = layerName; } - TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName]; + TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}]; timeStatsLayer.totalFrames++; timeStatsLayer.droppedFrames += layerRecord.droppedFrames; timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames; @@ -462,8 +477,13 @@ static bool layerNameIsValid(const std::string& layerName) { layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0; } +bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName) { + return mTimeStats.stats.count({uid, layerName}) > 0 || + mTimeStats.stats.size() < MAX_NUM_LAYER_STATS; +} + void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, - nsecs_t postTime) { + uid_t uid, nsecs_t postTime) { if (!mEnabled.load()) return; ATRACE_CALL(); @@ -471,11 +491,12 @@ void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::st postTime); std::lock_guard<std::mutex> lock(mMutex); - if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) { + if (!canAddNewAggregatedStats(uid, layerName)) { return; } if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS && layerNameIsValid(layerName)) { + mTimeStatsTracker[layerId].uid = uid; mTimeStatsTracker[layerId].layerName = layerName; } if (!mTimeStatsTracker.count(layerId)) return; @@ -655,6 +676,66 @@ void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber, flushAvailableRecordsToStatsLocked(layerId); } +template <class T> +static void updateJankPayload(T& t, int32_t reasons) { + t.jankPayload.totalFrames++; + + static const constexpr int32_t kValidJankyReason = + TimeStats::JankType::SurfaceFlingerDeadlineMissed | + TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed | + TimeStats::JankType::AppDeadlineMissed | TimeStats::JankType::Display; + if (reasons & kValidJankyReason) { + t.jankPayload.totalJankyFrames++; + if ((reasons & TimeStats::JankType::SurfaceFlingerDeadlineMissed) != 0) { + t.jankPayload.totalSFLongCpu++; + } + if ((reasons & TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed) != 0) { + t.jankPayload.totalSFLongGpu++; + } + if ((reasons & TimeStats::JankType::Display) != 0) { + t.jankPayload.totalSFUnattributed++; + } + if ((reasons & TimeStats::JankType::AppDeadlineMissed) != 0) { + t.jankPayload.totalAppUnattributed++; + } + } +} + +void TimeStats::incrementJankyFrames(int32_t reasons) { + if (!mEnabled.load()) return; + + ATRACE_CALL(); + std::lock_guard<std::mutex> lock(mMutex); + + updateJankPayload<TimeStatsHelper::TimeStatsGlobal>(mTimeStats, reasons); +} + +void TimeStats::incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) { + if (!mEnabled.load()) return; + + ATRACE_CALL(); + std::lock_guard<std::mutex> lock(mMutex); + + // Only update layer stats if we're allowed to do so. + // As an implementation detail, we do this because this method is expected to be + // called from FrameTimeline, which is allowed to do jank analysis well after a frame is + // presented. This means that we can't rely on TimeStats to flush layer records over to the + // aggregated stats. + if (!canAddNewAggregatedStats(uid, layerName)) { + return; + } + + // Defensively initialize the stats in case FrameTimeline flushes its signaled present fences + // before TimeStats does. + if (!mTimeStats.stats.count({uid, layerName})) { + mTimeStats.stats[{uid, layerName}].uid = uid; + mTimeStats.stats[{uid, layerName}].layerName = layerName; + } + + TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}]; + updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, reasons); +} + void TimeStats::onDestroy(int32_t layerId) { ATRACE_CALL(); ALOGV("[%d]-onDestroy", layerId); @@ -860,6 +941,7 @@ void TimeStats::clearGlobalLocked() { mTimeStats.presentToPresent.hist.clear(); mTimeStats.frameDuration.hist.clear(); mTimeStats.renderEngineTiming.hist.clear(); + mTimeStats.jankPayload = TimeStatsHelper::JankPayload(); mTimeStats.refreshRateStats.clear(); mPowerTime.prevTime = systemTime(); mGlobalRecord.prevPresentTime = 0; @@ -905,6 +987,3 @@ void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, std::strin } // namespace impl } // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index 8de5d0c3e8..4fa0a02770 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -17,6 +17,7 @@ #pragma once // TODO(b/129481165): remove the #pragma below and fix conversion issues +#include <cstdint> #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" @@ -85,7 +86,7 @@ public: const std::shared_ptr<FenceTime>& readyFence) = 0; virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, - nsecs_t postTime) = 0; + uid_t uid, nsecs_t postTime) = 0; virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0; // Reasons why latching a particular buffer may be skipped enum class LatchSkipReason { @@ -108,6 +109,40 @@ public: virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0; virtual void setPresentFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence) = 0; + + // Subset of jank metadata tracked by FrameTimeline for the purpose of funneling to telemetry. + enum JankType { + // No Jank + None = 0x0, + // Jank not related to SurfaceFlinger or the App + Display = 0x1, + // SF took too long on the CPU + SurfaceFlingerDeadlineMissed = 0x2, + // SF took too long on the GPU + SurfaceFlingerGpuDeadlineMissed = 0x4, + // Either App or GPU took too long on the frame + AppDeadlineMissed = 0x8, + // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a + // jank + // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame. + PredictionExpired = 0x10, + // Latching a buffer early might cause an early present of the frame + SurfaceFlingerEarlyLatch = 0x20, + }; + + // Increments janky frames, tracked globally. Because FrameTimeline is the infrastructure + // responsible for computing jank in the system, this is expected to be called from + // FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there are no + // jank reasons, then total frames are incremented but jank is not, for accurate accounting of + // janky frames. + virtual void incrementJankyFrames(int32_t reasons) = 0; + // Increments janky frames, blamed to the provided {uid, layerName} key, with JankMetadata as + // supplementary reasons for the jank. Because FrameTimeline is the infrastructure responsible + // for computing jank in the system, this is expected to be called from FrameTimeline, rather + // than directly from SurfaceFlinger or individual layers. + // If there are no jank reasons, then total frames are incremented but jank is not, for accurate + // accounting of janky frames. + virtual void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) = 0; // Clean up the layer record virtual void onDestroy(int32_t layerId) = 0; // If SF skips or rejects a buffer, remove the corresponding TimeRecord. @@ -142,6 +177,7 @@ class TimeStats : public android::TimeStats { }; struct LayerRecord { + uid_t uid; std::string layerName; // This is the index in timeRecords, at which the timestamps for that // specific frame are still not fully received. This is not waiting for @@ -241,7 +277,7 @@ public: void recordRenderEngineDuration(nsecs_t startTime, const std::shared_ptr<FenceTime>& readyFence) override; - void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, + void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid, nsecs_t postTime) override; void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override; void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override; @@ -253,6 +289,8 @@ public: void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override; void setPresentFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence) override; + void incrementJankyFrames(int32_t reasons) override; + void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) override; // Clean up the layer record void onDestroy(int32_t layerId) override; // If SF skips or rejects a buffer, remove the corresponding TimeRecord. @@ -276,6 +314,7 @@ private: void flushAvailableRecordsToStatsLocked(int32_t layerId); void flushPowerTimeLocked(); void flushAvailableGlobalRecordsToStatsLocked(); + bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName); void enable(); void disable(); diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp index d77387a895..0fb748f1a6 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp +++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp @@ -77,14 +77,28 @@ std::string TimeStatsHelper::Histogram::toString() const { return result; } +std::string TimeStatsHelper::JankPayload::toString() const { + std::string result; + StringAppendF(&result, "totalTimelineFrames = %d\n", totalFrames); + StringAppendF(&result, "jankyFrames = %d\n", totalJankyFrames); + StringAppendF(&result, "sfLongCpuJankyFrames = %d\n", totalSFLongCpu); + StringAppendF(&result, "sfLongGpuJankyFrames = %d\n", totalSFLongGpu); + StringAppendF(&result, "sfUnattributedJankyFrame = %d\n", totalSFUnattributed); + StringAppendF(&result, "appUnattributedJankyFrame = %d\n", totalAppUnattributed); + return result; +} + std::string TimeStatsHelper::TimeStatsLayer::toString() const { std::string result = "\n"; + StringAppendF(&result, "uid = %d\n", uid); StringAppendF(&result, "layerName = %s\n", layerName.c_str()); StringAppendF(&result, "packageName = %s\n", packageName.c_str()); StringAppendF(&result, "totalFrames = %d\n", totalFrames); StringAppendF(&result, "droppedFrames = %d\n", droppedFrames); StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames); StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames); + result.append("Jank payload for this layer:\n"); + result.append(jankPayload.toString()); const auto iter = deltas.find("present2present"); if (iter != deltas.end()) { const float averageTime = iter->second.averageTime(); @@ -110,6 +124,8 @@ std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> m StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitches); StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChanges); StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime); + result.append("Global aggregated jank payload:\n"); + result.append(jankPayload.toString()); StringAppendF(&result, "displayConfigStats is as below:\n"); for (const auto& [fps, duration] : refreshRateStats) { StringAppendF(&result, "%dfps = %ldms\n", fps, ns2ms(duration)); diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h index 0c75f96e46..033eb5dcd9 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h +++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h @@ -40,14 +40,28 @@ public: std::string toString() const; }; + struct JankPayload { + // note that transactions are counted for these frames. + int32_t totalFrames = 0; + int32_t totalJankyFrames = 0; + int32_t totalSFLongCpu = 0; + int32_t totalSFLongGpu = 0; + int32_t totalSFUnattributed = 0; + int32_t totalAppUnattributed = 0; + + std::string toString() const; + }; + class TimeStatsLayer { public: + uid_t uid; std::string layerName; std::string packageName; int32_t totalFrames = 0; int32_t droppedFrames = 0; int32_t lateAcquireFrames = 0; int32_t badDesiredPresentFrames = 0; + JankPayload jankPayload; std::unordered_map<std::string, Histogram> deltas; std::string toString() const; @@ -69,8 +83,17 @@ public: Histogram presentToPresent; Histogram frameDuration; Histogram renderEngineTiming; - std::unordered_map<std::string, TimeStatsLayer> stats; + + struct StatsHasher { + size_t operator()(const std::pair<uid_t, std::string>& p) const { + // Normally this isn't a very good hash function due to symmetry reasons, + // but these are distinct types so this should be good enough + return std::hash<uid_t>{}(p.first) ^ std::hash<std::string>{}(p.second); + } + }; + std::unordered_map<std::pair<uid_t, std::string>, TimeStatsLayer, StatsHasher> stats; std::unordered_map<uint32_t, nsecs_t> refreshRateStats; + JankPayload jankPayload; std::string toString(std::optional<uint32_t> maxLayers) const; SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const; diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto index f3f56265e0..9f25674f1b 100644 --- a/services/surfaceflinger/layerproto/layers.proto +++ b/services/surfaceflinger/layerproto/layers.proto @@ -125,6 +125,9 @@ message LayerProto { int32 background_blur_radius = 52; uint32 owner_uid = 53; + + // Regions of a layer, where blur should be applied. + repeated BlurRegion blur_regions = 54; } message PositionProto { @@ -211,3 +214,16 @@ message ColorTransformProto { // This will be a 4x4 matrix of float values repeated float val = 1; } + +message BlurRegion { + uint32 blur_radius = 1; + uint32 corner_radius_tl = 2; + uint32 corner_radius_tr = 3; + uint32 corner_radius_bl = 4; + float corner_radius_br = 5; + float alpha = 6; + int32 left = 7; + int32 top = 8; + int32 right = 9; + int32 bottom = 10; +}
\ No newline at end of file diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp index a5a569bb0f..81e648aaef 100644 --- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp +++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp @@ -51,6 +51,7 @@ constexpr int BACKGROUND_BLUR_RADIUS_UPDATE = 24; constexpr float POSITION_UPDATE = 121; const Rect CROP_UPDATE(16, 16, 32, 32); const float SHADOW_RADIUS_UPDATE = 35.0f; +std::vector<BlurRegion> BLUR_REGIONS_UPDATE; const String8 DISPLAY_NAME("SurfaceInterceptor Display Test"); constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface"; @@ -181,6 +182,7 @@ public: bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius); bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change, bool foundBackgroundBlurRadius); + bool blurRegionsUpdateFound(const SurfaceChange& change, bool foundBlurRegions); bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix); bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode); bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion); @@ -219,6 +221,7 @@ public: void cropUpdate(Transaction&); void cornerRadiusUpdate(Transaction&); void backgroundBlurRadiusUpdate(Transaction&); + void blurRegionsUpdate(Transaction&); void matrixUpdate(Transaction&); void transparentRegionHintUpdate(Transaction&); void layerStackUpdate(Transaction&); @@ -357,6 +360,12 @@ void SurfaceInterceptorTest::backgroundBlurRadiusUpdate(Transaction& t) { t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE); } +void SurfaceInterceptorTest::blurRegionsUpdate(Transaction& t) { + BLUR_REGIONS_UPDATE.empty(); + BLUR_REGIONS_UPDATE.push_back(BlurRegion()); + t.setBlurRegions(mBGSurfaceControl, BLUR_REGIONS_UPDATE); +} + void SurfaceInterceptorTest::layerUpdate(Transaction& t) { t.setLayer(mBGSurfaceControl, LAYER_UPDATE); } @@ -430,6 +439,7 @@ void SurfaceInterceptorTest::runAllUpdates() { runInTransaction(&SurfaceInterceptorTest::alphaUpdate); runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate); runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate); + runInTransaction(&SurfaceInterceptorTest::blurRegionsUpdate); runInTransaction(&SurfaceInterceptorTest::layerUpdate); runInTransaction(&SurfaceInterceptorTest::cropUpdate); runInTransaction(&SurfaceInterceptorTest::matrixUpdate); @@ -518,6 +528,17 @@ bool SurfaceInterceptorTest::backgroundBlurRadiusUpdateFound(const SurfaceChange return foundBackgroundBlur; } +bool SurfaceInterceptorTest::blurRegionsUpdateFound(const SurfaceChange& change, + bool foundBlurRegions) { + bool hasBlurRegions(change.blur_regions().blur_regions_size() == BLUR_REGIONS_UPDATE.size()); + if (hasBlurRegions && !foundBlurRegions) { + foundBlurRegions = true; + } else if (hasBlurRegions && foundBlurRegions) { + []() { FAIL(); }(); + } + return foundBlurRegions; +} + bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) { bool hasLayer(change.layer().layer() == LAYER_UPDATE); if (hasLayer && !foundLayer) { @@ -706,6 +727,9 @@ bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace, case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius: foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate); break; + case SurfaceChange::SurfaceChangeCase::kBlurRegions: + foundUpdate = blurRegionsUpdateFound(change, foundUpdate); + break; case SurfaceChange::SurfaceChangeCase::kMatrix: foundUpdate = matrixUpdateFound(change, foundUpdate); break; @@ -889,6 +913,11 @@ TEST_F(SurfaceInterceptorTest, InterceptBackgroundBlurRadiusUpdateWorks) { SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius); } +TEST_F(SurfaceInterceptorTest, InterceptBlurRegionsUpdateWorks) { + captureTest(&SurfaceInterceptorTest::blurRegionsUpdate, + SurfaceChange::SurfaceChangeCase::kBlurRegions); +} + TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) { captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix); } diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp index 2861013346..8d094e46ef 100644 --- a/services/surfaceflinger/tests/fakehwc/Android.bp +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -8,6 +8,7 @@ cc_test { "FakeComposerUtils.cpp", "SFFakeHwc_test.cpp" ], + require_root: true, shared_libs: [ "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 69efd7f477..5e9d64af0f 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "gmock/gmock-spec-builders.h" +#include "mock/MockTimeStats.h" #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" @@ -23,6 +25,11 @@ #include <cinttypes> using namespace std::chrono_literals; +using testing::Contains; + +MATCHER_P(HasBit, bit, "") { + return (arg & bit) != 0; +} namespace android::frametimeline { @@ -41,7 +48,8 @@ public: } void SetUp() override { - mFrameTimeline = std::make_unique<impl::FrameTimeline>(); + mTimeStats = std::make_shared<mock::TimeStats>(); + mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats); mTokenManager = &mFrameTimeline->mTokenManager; maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames; maxTokenRetentionTime = mTokenManager->kMaxRetentionTime; @@ -76,6 +84,7 @@ public: return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size()); } + std::shared_ptr<mock::TimeStats> mTimeStats; std::unique_ptr<impl::FrameTimeline> mFrameTimeline; impl::TokenManager* mTokenManager; FenceToFenceTimeMap fenceFactory; @@ -83,6 +92,10 @@ public: nsecs_t maxTokenRetentionTime; }; +static const std::string sLayerNameOne = "layer1"; +static const std::string sLayerNameTwo = "layer2"; +static constexpr const uid_t sUidOne = 0; + TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) { int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0}); EXPECT_EQ(getPredictions().size(), 1); @@ -99,21 +112,24 @@ TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) { } TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) { - auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt); + auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, + sLayerNameOne, std::nullopt); EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None); } TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) { int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0}); flushTokens(systemTime() + maxTokenRetentionTime); - auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", token1); + auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, + sLayerNameOne, token1); EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired); } TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) { int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); - auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", token1); + auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, + sLayerNameOne, token1); EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid); EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true); @@ -125,7 +141,8 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) { int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60}); - auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken("layer1", token1); + auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, + sLayerNameOne, token1); // Set up the display frame mFrameTimeline->setSfWakeUp(token1, 20); @@ -148,8 +165,12 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) { int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30}); int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 60}); - auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken1); - auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken("layer2", surfaceFrameToken1); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne, + surfaceFrameToken1); + auto surfaceFrame2 = + mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameTwo, sLayerNameTwo, + surfaceFrameToken1); mFrameTimeline->setSfWakeUp(sfToken1, 22); mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Presented); @@ -168,7 +189,9 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) { // Trigger a flush by finalizing the next DisplayFrame auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - auto surfaceFrame3 = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken2); + auto surfaceFrame3 = + mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne, + surfaceFrameToken2); mFrameTimeline->setSfWakeUp(sfToken2, 52); mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame3), SurfaceFrame::PresentState::Dropped); mFrameTimeline->setSfPresent(56, presentFence2); @@ -189,7 +212,9 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor}); int64_t sfToken = mTokenManager->generateTokenForPredictions( {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor}); - auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken); + auto surfaceFrame = + mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne, + surfaceFrameToken); mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor); mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), SurfaceFrame::PresentState::Presented); @@ -209,7 +234,9 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor}); int64_t sfToken = mTokenManager->generateTokenForPredictions( {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor}); - auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken); + auto surfaceFrame = + mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne, + surfaceFrameToken); mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor); mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), SurfaceFrame::PresentState::Presented); mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence); @@ -224,7 +251,8 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) { auto surfaceFrame = - mFrameTimeline->createSurfaceFrameForToken("acquireFenceAfterQueue", std::nullopt); + mFrameTimeline->createSurfaceFrameForToken(0, "acquireFenceAfterQueue", + "acquireFenceAfterQueue", std::nullopt); surfaceFrame->setActualQueueTime(123); surfaceFrame->setAcquireFenceTime(456); EXPECT_EQ(surfaceFrame->getActuals().endTime, 456); @@ -232,7 +260,8 @@ TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) { TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) { auto surfaceFrame = - mFrameTimeline->createSurfaceFrameForToken("acquireFenceAfterQueue", std::nullopt); + mFrameTimeline->createSurfaceFrameForToken(0, "acquireFenceAfterQueue", + "acquireFenceAfterQueue", std::nullopt); surfaceFrame->setActualQueueTime(456); surfaceFrame->setAcquireFenceTime(123); EXPECT_EQ(surfaceFrame->getActuals().endTime, 456); @@ -244,7 +273,8 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { // Size shouldn't exceed maxDisplayFrames - 64 for (size_t i = 0; i < *maxDisplayFrames + 10; i++) { - auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt); + auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, + sLayerNameOne, std::nullopt); int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30}); mFrameTimeline->setSfWakeUp(sfToken, 22); mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), @@ -258,7 +288,8 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { EXPECT_EQ(*maxDisplayFrames, 256); for (size_t i = 0; i < *maxDisplayFrames + 10; i++) { - auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt); + auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, + sLayerNameOne, std::nullopt); int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30}); mFrameTimeline->setSfWakeUp(sfToken, 22); mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), @@ -272,7 +303,8 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { EXPECT_EQ(*maxDisplayFrames, 128); for (size_t i = 0; i < *maxDisplayFrames + 10; i++) { - auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt); + auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, + sLayerNameOne, std::nullopt); int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30}); mFrameTimeline->setSfWakeUp(sfToken, 22); mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), @@ -281,4 +313,90 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { } EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames); } + +TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) { + EXPECT_CALL(*mTimeStats, + incrementJankyFrames(sUidOne, sLayerNameOne, + HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed))); + EXPECT_CALL(*mTimeStats, + incrementJankyFrames(HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed))); + auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()}); + int64_t sfToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()}); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne, + surfaceFrameToken1); + mFrameTimeline->setSfWakeUp(sfToken1, + std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count()); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), + SurfaceFrame::PresentState::Presented); + presentFence1->signalForTest( + std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()); + + mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(), + presentFence1); +} + +TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) { + EXPECT_CALL(*mTimeStats, + incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(TimeStats::JankType::Display))); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::Display))); + auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()}); + int64_t sfToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()}); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne, + surfaceFrameToken1); + mFrameTimeline->setSfWakeUp(sfToken1, + std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count()); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), + SurfaceFrame::PresentState::Presented); + presentFence1->signalForTest( + std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()); + mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(), + presentFence1); +} + +TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) { + EXPECT_CALL(*mTimeStats, + incrementJankyFrames(sUidOne, sLayerNameOne, + HasBit(TimeStats::JankType::AppDeadlineMissed))); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::AppDeadlineMissed))); + auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()}); + int64_t sfToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(), + std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()}); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne, + surfaceFrameToken1); + surfaceFrame1->setAcquireFenceTime( + std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count()); + mFrameTimeline->setSfWakeUp(sfToken1, + std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count()); + + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), + SurfaceFrame::PresentState::Presented); + presentFence1->signalForTest( + std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()); + mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(), + presentFence1); +} + } // namespace android::frametimeline diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index 0a246505ca..a90f4247a9 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -58,6 +58,7 @@ using PowerMode = hardware::graphics::composer::V2_4::IComposerClient::PowerMode #define FMT_STRING false #define LAYER_ID_0 0 #define LAYER_ID_1 1 +#define UID_0 123 #define LAYER_ID_INVALID -1 #define NUM_LAYERS 1 #define NUM_LAYERS_INVALID "INVALID" @@ -227,7 +228,8 @@ static std::string genLayerName(int32_t layerId) { void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) { switch (type) { case TimeStamp::POST: - ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts)); + ASSERT_NO_FATAL_FAILURE( + mTimeStats->setPostTime(id, frameNumber, genLayerName(id), UID_0, ts)); break; case TimeStamp::ACQUIRE: ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts)); @@ -349,6 +351,61 @@ TEST_F(TimeStatsTest, canIncreaseBadDesiredPresent) { EXPECT_THAT(result, HasSubstr(expectedResult)); } +TEST_F(TimeStatsTest, canIncreaseJankyFrames) { + // this stat is not in the proto so verify by checking the string dump + EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); + + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); + mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::Display); + mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::None); + + const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); + std::string expectedResult = "totalTimelineFrames = " + std::to_string(5); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "jankyFrames = " + std::to_string(4); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "appUnattributedJankyFrame = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); +} + +TEST_F(TimeStatsTest, canIncreaseJankyFramesForLayer) { + // this stat is not in the proto so verify by checking the string dump + EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); + + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::SurfaceFlingerDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::AppDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None); + + const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); + std::string expectedResult = "totalTimelineFrames = " + std::to_string(5); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "jankyFrames = " + std::to_string(4); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "appUnattributedJankyFrame = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); +} + TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) { // this stat is not in the proto so verify by checking the string dump constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2; @@ -789,6 +846,16 @@ TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) { .count()); mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>( std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count())); + + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::SurfaceFlingerDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::AppDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None); + EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty()); const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); @@ -797,6 +864,11 @@ TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) { EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0")); EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms")); EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms")); + EXPECT_THAT(result, HasSubstr("jankyFrames = 0")); + EXPECT_THAT(result, HasSubstr("sfLongCpuJankyFrames = 0")); + EXPECT_THAT(result, HasSubstr("sfLongGpuJankyFrames = 0")); + EXPECT_THAT(result, HasSubstr("sfUnattributedJankyFrame = 0")); + EXPECT_THAT(result, HasSubstr("appUnattributedJankyFrame = 0")); } TEST_F(TimeStatsTest, canDumpWithMaxLayers) { @@ -904,6 +976,8 @@ TEST_F(TimeStatsTest, globalStatsCallback) { mTimeStats->incrementClientCompositionFrames(); } + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); + mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS); mTimeStats->setPowerMode(PowerMode::ON); mTimeStats->recordFrameDuration(1000000, 3000000); @@ -913,6 +987,12 @@ TEST_F(TimeStatsTest, globalStatsCallback) { mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000)); mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000)); + mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::Display); + mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::None); + EXPECT_THAT(mDelegate->mAtomTags, UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, android::util::SURFACEFLINGER_STATS_LAYER_INFO)); @@ -944,6 +1024,12 @@ TEST_F(TimeStatsTest, globalStatsCallback) { expectedRenderEngineTiming.c_str(), expectedRenderEngineTiming.size()), expectedRenderEngineTiming.size())); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent)); } EXPECT_EQ(AStatsManager_PULL_SUCCESS, @@ -975,6 +1061,15 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { } insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::SurfaceFlingerDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::AppDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None); + EXPECT_THAT(mDelegate->mAtomTags, UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, android::util::SURFACEFLINGER_STATS_LAYER_INFO)); @@ -1033,6 +1128,14 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES)); EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, UID_0)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent)); } EXPECT_EQ(AStatsManager_PULL_SUCCESS, diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h index ff37ec8018..99ec353095 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h +++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h @@ -41,7 +41,7 @@ public: MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t)); MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t)); MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&)); - MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t)); + MOCK_METHOD5(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t)); MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason)); MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId)); MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t)); @@ -50,6 +50,8 @@ public: MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&)); MOCK_METHOD3(setPresentTime, void(int32_t, uint64_t, nsecs_t)); MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&)); + MOCK_METHOD1(incrementJankyFrames, void(int32_t)); + MOCK_METHOD3(incrementJankyFrames, void(uid_t, const std::string&, int32_t)); MOCK_METHOD1(onDestroy, void(int32_t)); MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t)); MOCK_METHOD1(setPowerMode, |