Merge "SF: add trace points for dispatcher system"
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 7f2f949..b574098 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -51,6 +51,7 @@
RelativeParentChange relative_parent = 18;
DetachChildrenChange detach_children = 19;
ReparentChildrenChange reparent_children = 20;
+ BackgroundBlurRadiusChange background_blur_radius = 21;
ShadowRadiusChange shadow_radius = 22;
}
}
@@ -73,6 +74,10 @@
required float corner_radius = 1;
}
+message BackgroundBlurRadiusChange {
+ required float background_blur_radius = 1;
+}
+
message LayerChange {
required uint32 layer = 1;
}
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 675aad6..2b5667d 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -510,6 +510,14 @@
t.setCornerRadius(mLayers[id], cc.corner_radius());
}
+void Replayer::setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
+ layer_id id, const BackgroundBlurRadiusChange& cc) {
+ ALOGV("Layer %d: Setting Background Blur Radius -- backgroundBlurRadius=%d", id,
+ cc.background_blur_radius());
+
+ t.setBackgroundBlurRadius(mLayers[id], cc.background_blur_radius());
+}
+
void Replayer::setMatrix(SurfaceComposerClient::Transaction& t,
layer_id id, const MatrixChange& mc) {
ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(),
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index b547834..95857e1 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -94,6 +94,8 @@
layer_id id, const CropChange& cc);
void setCornerRadius(SurfaceComposerClient::Transaction& t,
layer_id id, const CornerRadiusChange& cc);
+ void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
+ layer_id id, const BackgroundBlurRadiusChange& cc);
void setMatrix(SurfaceComposerClient::Transaction& t,
layer_id id, const MatrixChange& mc);
void setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
diff --git a/data/etc/android.hardware.context_hub.xml b/data/etc/android.hardware.context_hub.xml
new file mode 100644
index 0000000..8133e0b
--- /dev/null
+++ b/data/etc/android.hardware.context_hub.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<!-- Features for devices supporting a Context Hub. -->
+<permissions>
+ <feature name="android.hardware.context_hub" />
+</permissions>
diff --git a/data/etc/android.hardware.telephony.cdma.xml b/data/etc/android.hardware.telephony.cdma.xml
index b598f68..082378d 100644
--- a/data/etc/android.hardware.telephony.cdma.xml
+++ b/data/etc/android.hardware.telephony.cdma.xml
@@ -18,5 +18,4 @@
<permissions>
<feature name="android.hardware.telephony" />
<feature name="android.hardware.telephony.cdma" />
- <feature name="android.hardware.telephony.data" />
</permissions>
diff --git a/data/etc/android.hardware.telephony.gsm.xml b/data/etc/android.hardware.telephony.gsm.xml
index fe8a5cf..7927fa8 100644
--- a/data/etc/android.hardware.telephony.gsm.xml
+++ b/data/etc/android.hardware.telephony.gsm.xml
@@ -18,5 +18,4 @@
<permissions>
<feature name="android.hardware.telephony" />
<feature name="android.hardware.telephony.gsm" />
- <feature name="android.hardware.telephony.data" />
</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml
new file mode 100644
index 0000000..9c67d4a
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan deQP
+ tests associated with date 2019-03-01 (0x07E30301). -->
+<permissions>
+ <feature name="android.software.vulkan.deqp.level" version="132317953" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml
new file mode 100644
index 0000000..19b269b
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan deQP
+ tests associated with date 2020-03-01 (0x07E40301). -->
+<permissions>
+ <feature name="android.software.vulkan.deqp.level" version="132383489" />
+</permissions>
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index d920a90..571a5ca 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -79,6 +79,14 @@
ANDROID_BITMAP_FLAGS_ALPHA_SHIFT = 0,
};
+enum {
+ /** If this bit is set in AndroidBitmapInfo.flags, the Bitmap uses the
+ * HARDWARE Config, and its AHardwareBuffer can be retrieved via
+ * AndroidBitmap_getHardwareBuffer.
+ */
+ ANDROID_BITMAP_FLAGS_IS_HARDWARE = 1 << 31,
+};
+
/** Bitmap info, see AndroidBitmap_getInfo(). */
typedef struct {
/** The bitmap width in pixels. */
@@ -90,7 +98,9 @@
/** The bitmap pixel format. See {@link AndroidBitmapFormat} */
int32_t format;
/** Two bits are used to encode alpha. Use ANDROID_BITMAP_FLAGS_ALPHA_MASK
- * and ANDROID_BITMAP_FLAGS_ALPHA_SHIFT to retrieve them. */
+ * and ANDROID_BITMAP_FLAGS_ALPHA_SHIFT to retrieve them. One bit is used
+ * to encode whether the Bitmap uses the HARDWARE Config. Use
+ * ANDROID_BITMAP_FLAGS_IS_HARDWARE to know.*/
uint32_t flags;
} AndroidBitmapInfo;
@@ -179,7 +189,7 @@
* @param size Length in bytes of data to write.
* @return Whether the operation succeeded.
*/
-typedef bool (*AndroidBitmap_compress_write_fn)(void* userContext,
+typedef bool (*AndroidBitmap_CompressWriteFunc)(void* userContext,
const void* data,
size_t size) __INTRODUCED_IN(30);
@@ -195,7 +205,7 @@
* differently depending on the
* {@link AndroidBitmapCompressFormat}.
* @param userContext User-defined data which will be passed to the supplied
- * {@link AndroidBitmap_compress_write_fn} each time it is
+ * {@link AndroidBitmap_CompressWriteFunc} each time it is
* called. May be null.
* @parm fn Function that writes the compressed data. Will be called each time
* the compressor has compressed more data that is ready to be
@@ -208,7 +218,26 @@
const void* pixels,
int32_t format, int32_t quality,
void* userContext,
- AndroidBitmap_compress_write_fn fn) __INTRODUCED_IN(30);
+ AndroidBitmap_CompressWriteFunc fn) __INTRODUCED_IN(30);
+
+struct AHardwareBuffer;
+
+/**
+ * Retrieve the native object associated with a HARDWARE Bitmap.
+ *
+ * Client must not modify it while a Bitmap is wrapping it.
+ *
+ * @param bitmap Handle to an android.graphics.Bitmap.
+ * @param outBuffer On success, is set to a pointer to the
+ * AHardwareBuffer associated with bitmap. This acquires
+ * a reference on the buffer, and the client must call
+ * AHardwareBuffer_release when finished with it.
+ * @return AndroidBitmap functions result code.
+ * ANDROID_BITMAP_RESULT_BAD_PARAMETER if bitmap is not a
+ * HARDWARE Bitmap.
+ */
+int AndroidBitmap_getHardwareBuffer(JNIEnv* env, jobject bitmap,
+ AHardwareBuffer** outBuffer) __INTRODUCED_IN(30);
#endif // __ANDROID_API__ >= 30
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 4b6446c..469b088 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -159,6 +159,25 @@
int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, bool required) __INTRODUCED_IN(30);
/**
+ * Choose the dataspace for the output.
+ *
+ * Not supported for {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support
+ * an ADataSpace.
+ *
+ * @param dataspace The {@link ADataSpace} to decode into. An ADataSpace
+ * specifies how to interpret the colors. By default,
+ * AImageDecoder will decode into the ADataSpace specified by
+ * {@link AImageDecoderHeaderInfo_getDataSpace}. If this
+ * parameter is set to a different ADataSpace, AImageDecoder
+ * will transform the output into the specified ADataSpace.
+ * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for a null
+ * AImageDecoder or an integer that does not correspond to an
+ * ADataSpace value.
+ */
+int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_IN(30);
+
+/**
* Specify the output size for a decoded image.
*
* Future calls to {@link AImageDecoder_decodeImage} will sample or scale the
@@ -179,6 +198,28 @@
*/
int AImageDecoder_setTargetSize(AImageDecoder*, int width, int height) __INTRODUCED_IN(30);
+
+/**
+ * Compute the dimensions to use for a given sampleSize.
+ *
+ * Although AImageDecoder can scale to an arbitrary target size (see
+ * {@link AImageDecoder_setTargetSize}), some sizes may be more efficient than
+ * others. This computes the most efficient target size to use to reach a
+ * particular sampleSize.
+ *
+ * @param sampleSize A subsampling rate of the original image. Must be greater
+ * than or equal to 1. A sampleSize of 2 means to skip every
+ * other pixel/line, resulting in a width and height that are
+ * 1/2 of the original dimensions, with 1/4 the number of
+ * pixels.
+ * @param width Out parameter for the width sampled by sampleSize, and rounded
+ * direction that the decoder can do most efficiently.
+ * @param height Out parameter for the height sampled by sampleSize, and rounded
+ * direction that the decoder can do most efficiently.
+ * @return ANDROID_IMAGE_DECODER result code.
+ */
+int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize,
+ int* width, int* height) __INTRODUCED_IN(30);
/**
* Specify how to crop the output after scaling (if any).
*
@@ -261,6 +302,25 @@
const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
/**
+ * Report the dataspace the AImageDecoder will decode to by default.
+ * AImageDecoder will try to choose one that is sensible for the
+ * image and the system. Note that this may not exactly match the ICC
+ * profile (or other color information) stored in the encoded image.
+ *
+ * @return The {@link ADataSpace} most closely representing the way the colors
+ * are encoded (or {@link ADATASPACE_UNKNOWN} if there is not an
+ * approximate ADataSpace). This specifies how to interpret the colors
+ * in the decoded image, unless {@link AImageDecoder_setDataSpace} is
+ * called to decode to a different ADataSpace.
+ *
+ * Note that ADataSpace only exposes a few values. This may return
+ * ADATASPACE_UNKNOWN, even for Named ColorSpaces, if they have no
+ * corresponding ADataSpace.
+ */
+int32_t AImageDecoderHeaderInfo_getDataSpace(
+ const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
* Return the minimum stride that can be used, taking the specified
* (or default) (possibly scaled) width, crop rect and
* {@link AndroidBitmapFormat} into account.
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 5959340..55a892e 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -69,9 +69,6 @@
"SurfaceComposerClient.cpp",
"SyncFeatures.cpp",
"view/Surface.cpp",
- "surfacetexture/SurfaceTexture.cpp",
- "surfacetexture/ImageConsumer.cpp",
- "surfacetexture/EGLConsumer.cpp",
],
shared_libs: [
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 39b4d4b..5547efc 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -86,6 +86,7 @@
memcpy(output.writeInplace(16 * sizeof(float)),
colorTransform.asArray(), 16 * sizeof(float));
output.writeFloat(cornerRadius);
+ output.writeUint32(backgroundBlurRadius);
output.writeStrongBinder(cachedBuffer.token.promote());
output.writeUint64(cachedBuffer.id);
output.writeParcelable(metadata);
@@ -173,6 +174,7 @@
colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
cornerRadius = input.readFloat();
+ backgroundBlurRadius = input.readUint32();
cachedBuffer.token = input.readStrongBinder();
cachedBuffer.id = input.readUint64();
input.readParcelable(&metadata);
@@ -307,6 +309,10 @@
what |= eCornerRadiusChanged;
cornerRadius = other.cornerRadius;
}
+ if (other.what & eBackgroundBlurRadiusChanged) {
+ what |= eBackgroundBlurRadiusChanged;
+ backgroundBlurRadius = other.backgroundBlurRadius;
+ }
if (other.what & eDeferTransaction_legacy) {
what |= eDeferTransaction_legacy;
barrierHandle_legacy = other.barrierHandle_legacy;
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 274153c..c13401d 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,3 +1,6 @@
+adyabr@google.com
+akrulec@google.com
+alecmouri@google.com
jessehall@google.com
jwcai@google.com
lpy@google.com
@@ -6,3 +9,4 @@
racarr@google.com
steventhomas@google.com
stoza@google.com
+vhau@google.com
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 245aaf5..63dc333 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -918,6 +918,18 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundBlurRadius(
+ const sp<SurfaceControl>& sc, int backgroundBlurRadius) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eBackgroundBlurRadiusChanged;
+ s->backgroundBlurRadius = backgroundBlurRadius;
+ return *this;
+}
+
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
const sp<IBinder>& handle,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index cf64193..c256a09 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -101,6 +101,7 @@
eColorSpaceAgnosticChanged = 0x10'00000000,
eFrameRateSelectionPriority = 0x20'00000000,
eFrameRateChanged = 0x40'00000000,
+ eBackgroundBlurRadiusChanged = 0x80'00000000,
};
layer_state_t()
@@ -117,6 +118,7 @@
reserved(0),
crop_legacy(Rect::INVALID_RECT),
cornerRadius(0.0f),
+ backgroundBlurRadius(0),
frameNumber_legacy(0),
overrideScalingMode(-1),
transform(0),
@@ -163,6 +165,7 @@
matrix22_t matrix;
Rect crop_legacy;
float cornerRadius;
+ uint32_t backgroundBlurRadius;
sp<IBinder> barrierHandle_legacy;
sp<IBinder> reparentHandle;
uint64_t frameNumber_legacy;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 06f1c92..08e6a5a 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -431,6 +431,8 @@
float dsdx, float dtdx, float dtdy, float dsdy);
Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
+ Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
+ int backgroundBlurRadius);
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/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index bcc99fc..c956578 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -45,6 +45,10 @@
srcs: [
"AChoreographer.cpp",
"ADisplay.cpp",
+ "surfacetexture/surface_texture.cpp",
+ "surfacetexture/SurfaceTexture.cpp",
+ "surfacetexture/ImageConsumer.cpp",
+ "surfacetexture/EGLConsumer.cpp",
],
shared_libs: [
@@ -53,6 +57,10 @@
"libnativewindow",
"libui",
"libutils",
+ "libcutils",
+ "libEGL",
+ "libGLESv2",
+ "libnativehelper",
],
header_libs: [
diff --git a/libs/gui/include/gui/surfacetexture/EGLConsumer.h b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h
similarity index 100%
rename from libs/gui/include/gui/surfacetexture/EGLConsumer.h
rename to libs/nativedisplay/include/surfacetexture/EGLConsumer.h
diff --git a/libs/gui/include/gui/surfacetexture/ImageConsumer.h b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
similarity index 100%
rename from libs/gui/include/gui/surfacetexture/ImageConsumer.h
rename to libs/nativedisplay/include/surfacetexture/ImageConsumer.h
diff --git a/libs/gui/include/gui/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
similarity index 100%
rename from libs/gui/include/gui/surfacetexture/SurfaceTexture.h
rename to libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
diff --git a/libs/gui/include/gui/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
similarity index 85%
rename from libs/gui/include/gui/surfacetexture/surface_texture_platform.h
rename to libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
index 72f98ad..6a94a77 100644
--- a/libs/gui/include/gui/surfacetexture/surface_texture_platform.h
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -22,17 +22,11 @@
#include <system/graphics.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/surfacetexture/SurfaceTexture.h>
-
// This file provides a facade API on top of SurfaceTexture, which avoids using
// C++ types. This is still a C++ unstable API though. Ideally features here
// will be exposed via public NDK API and this file will be deleted.
-struct ASurfaceTexture {
- android::sp<android::SurfaceTexture> consumer;
- android::sp<android::IGraphicBufferProducer> producer;
-};
+struct ASurfaceTexture;
namespace android {
@@ -79,13 +73,6 @@
ASurfaceTexture_fenceWait fenceWait,
void* fencePassThroughHandle);
-/**
- * ASurfaceTexture_create creates an ASurfaceTexture, which is
- * a simple struct containing a SurfaceTexture and an IGraphicBufferProducer.
- */
-ASurfaceTexture* ASurfaceTexture_create(sp<SurfaceTexture> consumer,
- sp<IGraphicBufferProducer> producer);
-
} // namespace android
#endif // _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 650bdf1..483fb25 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -11,6 +11,8 @@
AChoreographer_destroy; # apex # introduced=30
AChoreographer_getFd; # apex # introduced=30
AChoreographer_handlePendingEvents; # apex # introduced=30
+ ASurfaceTexture_fromSurfaceTexture; # apex # introduced=30
+ ASurfaceTexture_release; # apex # introduced=30
local:
*;
};
@@ -30,5 +32,16 @@
android::ADisplayConfig_getFps*;
android::ADisplayConfig_getCompositorOffsetNanos*;
android::ADisplayConfig_getAppVsyncOffsetNanos*;
+ android::ASurfaceTexture_getCurrentTextureTarget*;
+ android::ASurfaceTexture_takeConsumerOwnership*;
+ android::ASurfaceTexture_releaseConsumerOwnership*;
+ android::ASurfaceTexture_dequeueBuffer*;
+ android::SurfaceTexture*;
};
+ ASurfaceTexture_acquireANativeWindow;
+ ASurfaceTexture_attachToGLContext;
+ ASurfaceTexture_detachFromGLContext;
+ ASurfaceTexture_getTimestamp;
+ ASurfaceTexture_getTransformMatrix;
+ ASurfaceTexture_updateTexImage;
} LIBNATIVEDISPLAY;
diff --git a/libs/gui/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
similarity index 99%
rename from libs/gui/surfacetexture/EGLConsumer.cpp
rename to libs/nativedisplay/surfacetexture/EGLConsumer.cpp
index 1f0f52c..2f31888 100644
--- a/libs/gui/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -24,8 +24,8 @@
#include <cutils/compiler.h>
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
-#include <gui/surfacetexture/EGLConsumer.h>
-#include <gui/surfacetexture/SurfaceTexture.h>
+#include <surfacetexture/EGLConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
#include <inttypes.h>
#include <private/gui/SyncFeatures.h>
#include <utils/Log.h>
diff --git a/libs/gui/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
similarity index 97%
rename from libs/gui/surfacetexture/ImageConsumer.cpp
rename to libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index 4bc4a7b..16afc68 100644
--- a/libs/gui/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -15,8 +15,8 @@
*/
#include <gui/BufferQueue.h>
-#include <gui/surfacetexture/ImageConsumer.h>
-#include <gui/surfacetexture/SurfaceTexture.h>
+#include <surfacetexture/ImageConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
// Macro for including the SurfaceTexture name in log messages
#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
diff --git a/libs/gui/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
similarity index 90%
rename from libs/gui/surfacetexture/SurfaceTexture.cpp
rename to libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index 25e5618..62db6d0 100644
--- a/libs/gui/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -16,9 +16,9 @@
#include <cutils/compiler.h>
#include <gui/BufferQueue.h>
-#include <gui/surfacetexture/ImageConsumer.h>
-#include <gui/surfacetexture/SurfaceTexture.h>
-#include <gui/surfacetexture/surface_texture_platform.h>
+#include <surfacetexture/ImageConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
+#include <surfacetexture/surface_texture_platform.h>
#include <math/mat4.h>
#include <system/window.h>
#include <utils/Trace.h>
@@ -487,42 +487,4 @@
return buffer;
}
-unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) {
- return st->consumer->getCurrentTextureTarget();
-}
-
-void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* texture) {
- texture->consumer->takeConsumerOwnership();
-}
-
-void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) {
- texture->consumer->releaseConsumerOwnership();
-}
-
-AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
- android_dataspace* outDataspace,
- float* outTransformMatrix, bool* outNewContent,
- ASurfaceTexture_createReleaseFence createFence,
- ASurfaceTexture_fenceWait fenceWait, void* handle) {
- sp<GraphicBuffer> buffer;
- *outNewContent = false;
- bool queueEmpty;
- do {
- buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
- &queueEmpty, createFence, fenceWait, handle);
- if (!queueEmpty) {
- *outNewContent = true;
- }
- } while (buffer.get() && (!queueEmpty));
- return reinterpret_cast<AHardwareBuffer*>(buffer.get());
-}
-
-ASurfaceTexture* ASurfaceTexture_create(sp<SurfaceTexture> consumer,
- sp<IGraphicBufferProducer> producer) {
- ASurfaceTexture* ast = new ASurfaceTexture;
- ast->consumer = consumer;
- ast->producer = producer;
- return ast;
-}
-
} // namespace android
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
new file mode 100644
index 0000000..1670fbb
--- /dev/null
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <android/surface_texture.h>
+#include <android/surface_texture_jni.h>
+
+#define LOG_TAG "ASurfaceTexture"
+
+#include <utils/Log.h>
+
+#include <gui/Surface.h>
+
+#include <surfacetexture/surface_texture_platform.h>
+#include <surfacetexture/SurfaceTexture.h>
+
+#include <mutex>
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+struct ASurfaceTexture {
+ android::sp<android::SurfaceTexture> consumer;
+ android::sp<android::IGraphicBufferProducer> producer;
+};
+
+using namespace android;
+
+const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTexture";
+
+struct fields_t {
+ jfieldID surfaceTexture;
+ jfieldID producer;
+};
+static fields_t fields;
+static std::once_flag sInitFieldsOnce;
+
+#define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"
+#define ANDROID_GRAPHICS_PRODUCER_JNI_ID "mProducer"
+
+static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz)
+{
+ fields.surfaceTexture = env->GetFieldID(clazz,
+ ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID, "J");
+ if (fields.surfaceTexture == NULL) {
+ ALOGE("can't find android/graphics/SurfaceTexture.%s",
+ ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID);
+ }
+ fields.producer = env->GetFieldID(clazz,
+ ANDROID_GRAPHICS_PRODUCER_JNI_ID, "J");
+ if (fields.producer == NULL) {
+ ALOGE("can't find android/graphics/SurfaceTexture.%s",
+ ANDROID_GRAPHICS_PRODUCER_JNI_ID);
+ }
+}
+
+static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {
+ jclass clazz = env->FindClass(class_name);
+ LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name);
+ return clazz;
+}
+
+static void register_android_graphics_SurfaceTexture(JNIEnv* env)
+{
+ // Cache some fields.
+ ScopedLocalRef<jclass> klass(env, FindClassOrDie(env, kSurfaceTextureClassPathName));
+ SurfaceTexture_classInit(env, klass.get());
+}
+
+static bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) {
+ std::call_once(sInitFieldsOnce, [=]() {
+ register_android_graphics_SurfaceTexture(env);
+ });
+
+ jclass surfaceTextureClass = env->FindClass(kSurfaceTextureClassPathName);
+ return env->IsInstanceOf(thiz, surfaceTextureClass);
+}
+
+static sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) {
+ std::call_once(sInitFieldsOnce, [=]() {
+ register_android_graphics_SurfaceTexture(env);
+ });
+
+ return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture);
+}
+
+static sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) {
+ std::call_once(sInitFieldsOnce, [=]() {
+ register_android_graphics_SurfaceTexture(env);
+ });
+
+ return (IGraphicBufferProducer*)env->GetLongField(thiz, fields.producer);
+}
+
+// The following functions implement NDK API.
+ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture) {
+ if (!surfacetexture || !android_SurfaceTexture_isInstanceOf(env, surfacetexture)) {
+ return nullptr;
+ }
+ ASurfaceTexture* ast = new ASurfaceTexture;
+ ast->consumer = SurfaceTexture_getSurfaceTexture(env, surfacetexture);
+ ast->producer = SurfaceTexture_getProducer(env, surfacetexture);
+ return ast;
+}
+
+ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) {
+ sp<Surface> surface = new Surface(st->producer);
+ ANativeWindow* win(surface.get());
+ ANativeWindow_acquire(win);
+ return win;
+}
+
+void ASurfaceTexture_release(ASurfaceTexture* st) {
+ delete st;
+}
+
+int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t tex) {
+ return st->consumer->attachToContext(tex);
+}
+
+int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) {
+ return st->consumer->detachFromContext();
+}
+
+int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) {
+ return st->consumer->updateTexImage();
+}
+
+void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) {
+ st->consumer->getTransformMatrix(mtx);
+}
+
+int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) {
+ return st->consumer->getTimestamp();
+}
+
+// The following functions are private/unstable API.
+namespace android {
+
+unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) {
+ return st->consumer->getCurrentTextureTarget();
+}
+
+void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* texture) {
+ texture->consumer->takeConsumerOwnership();
+}
+
+void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) {
+ texture->consumer->releaseConsumerOwnership();
+}
+
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
+ android_dataspace* outDataspace,
+ float* outTransformMatrix, bool* outNewContent,
+ ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait, void* handle) {
+ sp<GraphicBuffer> buffer;
+ *outNewContent = false;
+ bool queueEmpty;
+ do {
+ buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
+ &queueEmpty, createFence, fenceWait, handle);
+ if (!queueEmpty) {
+ *outNewContent = true;
+ }
+ } while (buffer.get() && (!queueEmpty));
+ return reinterpret_cast<AHardwareBuffer*>(buffer.get());
+}
+
+} // namespace android
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 348377e..2e3ab4c 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -57,6 +57,10 @@
"gl/ImageManager.cpp",
"gl/Program.cpp",
"gl/ProgramCache.cpp",
+ "gl/filters/BlurFilter.cpp",
+ "gl/filters/LensBlurFilter.cpp",
+ "gl/filters/GaussianBlurFilter.cpp",
+ "gl/filters/GenericProgram.cpp",
],
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 0748dfb..09659fe 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -49,6 +49,9 @@
#include "GLShadowVertexGenerator.h"
#include "Program.h"
#include "ProgramCache.h"
+#include "filters/BlurFilter.h"
+#include "filters/GaussianBlurFilter.h"
+#include "filters/LensBlurFilter.h"
extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
@@ -422,6 +425,18 @@
mTraceGpuCompletion = true;
mFlushTracer = std::make_unique<FlushTracer>(this);
}
+
+ if (args.supportsBackgroundBlur) {
+ char isGaussian[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.gaussianBlur", isGaussian, "1");
+ if (atoi(isGaussian)) {
+ mBlurFilter = new GaussianBlurFilter(*this);
+ } else {
+ mBlurFilter = new LensBlurFilter(*this);
+ }
+ checkErrors("BlurFilter creation");
+ }
+
mImageManager = std::make_unique<ImageManager>(this);
mDrawingBuffer = createFramebuffer();
}
@@ -871,11 +886,19 @@
}
void GLESRenderEngine::checkErrors() const {
+ checkErrors(nullptr);
+}
+
+void GLESRenderEngine::checkErrors(const char* tag) const {
do {
// there could be more than one error flag
GLenum error = glGetError();
if (error == GL_NO_ERROR) break;
- ALOGE("GL error 0x%04x", int(error));
+ if (tag == nullptr) {
+ ALOGE("GL error 0x%04x", int(error));
+ } else {
+ ALOGE("GL error: %s -> 0x%04x", tag, int(error));
+ }
} while (true);
}
@@ -957,13 +980,36 @@
return BAD_VALUE;
}
- BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache);
+ std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
+ // Let's find the topmost layer requesting background blur (if any.)
+ // Blurs in multiple layers are not supported, given the cost of the shader.
+ const LayerSettings* blurLayer = nullptr;
+ if (CC_LIKELY(mBlurFilter != nullptr)) {
+ for (auto const& layer : layers) {
+ if (layer.backgroundBlurRadius > 0) {
+ blurLayer = &layer;
+ }
+ }
+ }
- if (fbo.getStatus() != NO_ERROR) {
- ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
- buffer->handle);
- checkErrors();
- return fbo.getStatus();
+ if (blurLayer == nullptr) {
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache);
+ if (fbo->getStatus() != NO_ERROR) {
+ ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors();
+ return fbo->getStatus();
+ }
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+ } else {
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+ auto status = mBlurFilter->setAsDrawTarget(display);
+ if (status != NO_ERROR) {
+ ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors();
+ return status;
+ }
}
// clear the entire buffer, sometimes when we reuse buffers we'd persist
@@ -973,8 +1019,6 @@
// opaque layers.
clearWithColor(0.0, 0.0, 0.0, 0.0);
- setViewportAndProjection(display.physicalDisplay, display.clip);
-
setOutputDataSpace(display.outputDataspace);
setDisplayMaxLuminance(display.maxLuminance);
@@ -991,7 +1035,36 @@
.setTexCoords(2 /* size */)
.setCropCoords(2 /* size */)
.build();
- for (auto layer : layers) {
+ for (auto const& layer : layers) {
+ if (blurLayer == &layer) {
+ auto status = mBlurFilter->prepare(layer.backgroundBlurRadius);
+ if (status != NO_ERROR) {
+ ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors("Can't render first blur pass");
+ return status;
+ }
+
+ fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
+ useFramebufferCache);
+ status = fbo->getStatus();
+ if (status != NO_ERROR) {
+ ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors("Can't bind native framebuffer");
+ return status;
+ }
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+
+ status = mBlurFilter->render();
+ if (status != NO_ERROR) {
+ ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
+ checkErrors("Can't render blur filter");
+ return status;
+ }
+ }
+
mState.maxMasteringLuminance = layer.source.buffer.maxMasteringLuminance;
mState.maxContentLuminance = layer.source.buffer.maxContentLuminance;
mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index f41eda2..547235a 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -46,6 +46,7 @@
namespace gl {
class GLImage;
+class BlurFilter;
class GLESRenderEngine : public impl::RenderEngine {
public:
@@ -117,6 +118,7 @@
std::unique_ptr<Framebuffer> createFramebuffer();
std::unique_ptr<Image> createImage();
void checkErrors() const;
+ void checkErrors(const char* tag) const;
void setScissor(const Rect& region);
void disableScissor();
bool waitSync(EGLSyncKHR sync, EGLint flags);
@@ -228,6 +230,9 @@
std::unique_ptr<Framebuffer> mDrawingBuffer;
+ // Blur effect processor, only instantiated when a layer requests it.
+ BlurFilter* mBlurFilter = nullptr;
+
class FlushTracer {
public:
FlushTracer(GLESRenderEngine* engine);
@@ -251,6 +256,11 @@
};
friend class FlushTracer;
friend class ImageManager;
+ friend class GLFramebuffer;
+ friend class BlurFilter;
+ friend class GaussianBlurFilter;
+ friend class LensBlurFilter;
+ friend class GenericProgram;
std::unique_ptr<FlushTracer> mFlushTracer;
std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
};
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 5fbb5ba..091eac9 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -20,8 +20,8 @@
#include <GLES/gl.h>
#include <GLES/glext.h>
-#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
#include <gui/DebugEGLImageTracker.h>
#include <nativebase/nativebase.h>
#include <utils/Trace.h>
@@ -32,14 +32,23 @@
namespace gl {
GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine)
+ : GLFramebuffer(engine, false /* multiTarget */) {}
+
+GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine, bool multiTarget)
: mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
glGenTextures(1, &mTextureName);
+ if (multiTarget) {
+ glGenTextures(1, &mSecondaryTextureName);
+ }
glGenFramebuffers(1, &mFramebufferName);
}
GLFramebuffer::~GLFramebuffer() {
glDeleteFramebuffers(1, &mFramebufferName);
glDeleteTextures(1, &mTextureName);
+ if (mSecondaryTextureName != -1) {
+ glDeleteTextures(1, &mSecondaryTextureName);
+ }
}
bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
@@ -68,6 +77,55 @@
return true;
}
+void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height) {
+ ATRACE_CALL();
+
+ glBindTexture(GL_TEXTURE_2D, mTextureName);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
+
+ const bool multiTarget = mSecondaryTextureName != -1;
+ if (multiTarget) {
+ glBindTexture(GL_TEXTURE_2D, mSecondaryTextureName);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
+ }
+
+ mBufferHeight = height;
+ mBufferWidth = width;
+ mEngine.checkErrors("Allocating Fbo texture");
+
+ bind();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0);
+ if (multiTarget) {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D,
+ mSecondaryTextureName, 0);
+ GLenum buffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
+ glDrawBuffers(2, buffers);
+ }
+ mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ unbind();
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ if (mStatus != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Frame buffer is not complete. Error %d", mStatus);
+ }
+}
+
+void GLFramebuffer::bind() const {
+ glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::unbind() const {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
} // namespace gl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index b7650bb..668685a 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -20,6 +20,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
#include <renderengine/Framebuffer.h>
struct ANativeWindowBuffer;
@@ -33,22 +34,30 @@
class GLFramebuffer : public renderengine::Framebuffer {
public:
explicit GLFramebuffer(GLESRenderEngine& engine);
+ explicit GLFramebuffer(GLESRenderEngine& engine, bool multiTarget);
~GLFramebuffer() override;
bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
const bool useFramebufferCache) override;
+ void allocateBuffers(uint32_t width, uint32_t height);
EGLImageKHR getEGLImage() const { return mEGLImage; }
uint32_t getTextureName() const { return mTextureName; }
+ uint32_t getSecondaryTextureName() const { return mSecondaryTextureName; }
uint32_t getFramebufferName() const { return mFramebufferName; }
int32_t getBufferHeight() const { return mBufferHeight; }
int32_t getBufferWidth() const { return mBufferWidth; }
+ GLenum getStatus() const { return mStatus; }
+ void bind() const;
+ void unbind() const;
private:
GLESRenderEngine& mEngine;
EGLDisplay mEGLDisplay;
EGLImageKHR mEGLImage;
bool usingFramebufferCache = false;
+ GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED;
uint32_t mTextureName, mFramebufferName;
+ uint32_t mSecondaryTextureName = -1;
int32_t mBufferHeight = 0;
int32_t mBufferWidth = 0;
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
new file mode 100644
index 0000000..a554687
--- /dev/null
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BlurFilter.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <ui/GraphicTypes.h>
+#include <cstdint>
+
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+BlurFilter::BlurFilter(GLESRenderEngine& engine)
+ : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mSimpleProgram(engine) {
+ mSimpleProgram.compile(getVertexShader(), getSimpleFragShader());
+ mSPosLoc = mSimpleProgram.getAttributeLocation("aPosition");
+ mSUvLoc = mSimpleProgram.getAttributeLocation("aUV");
+ mSTextureLoc = mSimpleProgram.getUniformLocation("uTexture");
+}
+
+status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display) {
+ ATRACE_NAME("BlurFilter::setAsDrawTarget");
+
+ if (!mTexturesAllocated) {
+ const uint32_t fboWidth = floorf(display.physicalDisplay.width() * kFboScale);
+ const uint32_t fboHeight = floorf(display.physicalDisplay.height() * kFboScale);
+ mCompositionFbo.allocateBuffers(fboWidth, fboHeight);
+ mBlurredFbo.allocateBuffers(fboWidth, fboHeight);
+ allocateTextures();
+ mTexturesAllocated = true;
+ }
+
+ if (mBlurredFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid blur buffer");
+ return mBlurredFbo.getStatus();
+ }
+ if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid composition buffer");
+ return mCompositionFbo.getStatus();
+ }
+
+ mCompositionFbo.bind();
+ glViewport(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight());
+ return NO_ERROR;
+}
+
+void BlurFilter::drawMesh(GLuint uv, GLuint position) {
+ GLfloat positions[] = {-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f};
+ GLfloat texCoords[] = {0.0, 0.0, 0.0, 1.0f, 1.0f, 1.0f, 1.0f, 0};
+
+ // set attributes
+ glEnableVertexAttribArray(uv);
+ glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0, texCoords);
+ glEnableVertexAttribArray(position);
+ glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
+ positions);
+
+ // draw mesh
+ glDrawArrays(GL_TRIANGLE_FAN, 0 /* first */, 4 /* count */);
+ mEngine.checkErrors("Drawing blur mesh");
+}
+
+status_t BlurFilter::render() {
+ ATRACE_NAME("BlurFilter::render");
+
+ // Now let's scale our blur up
+ mSimpleProgram.useProgram();
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
+ glUniform1i(mSTextureLoc, 0);
+ mEngine.checkErrors("Setting final pass uniforms");
+
+ drawMesh(mSUvLoc, mSPosLoc);
+
+ glUseProgram(0);
+ return NO_ERROR;
+}
+
+string BlurFilter::getVertexShader() const {
+ return R"SHADER(
+ #version 310 es
+ precision lowp float;
+
+ in vec2 aPosition;
+ in mediump vec2 aUV;
+ out mediump vec2 vUV;
+
+ void main() {
+ vUV = aUV;
+ gl_Position = vec4(aPosition, 0.0, 1.0);
+ }
+ )SHADER";
+}
+
+string BlurFilter::getSimpleFragShader() const {
+ string shader = R"SHADER(
+ #version 310 es
+ precision lowp float;
+
+ in mediump vec2 vUV;
+ out vec4 fragColor;
+
+ uniform sampler2D uTexture;
+
+ void main() {
+ fragColor = texture(uTexture, vUV);
+ }
+ )SHADER";
+ return shader;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
new file mode 100644
index 0000000..2b5ea58
--- /dev/null
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 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 <ui/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+#include "GenericProgram.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class BlurFilter {
+public:
+ // Downsample FBO to improve performance
+ static constexpr float kFboScale = 0.35f;
+
+ explicit BlurFilter(GLESRenderEngine& engine);
+ virtual ~BlurFilter(){};
+
+ // Set up render targets, redirecting output to offscreen texture.
+ status_t setAsDrawTarget(const DisplaySettings&);
+ // Allocate any textures needed for the filter.
+ virtual void allocateTextures() = 0;
+ // Execute blur passes, rendering to offscreen texture.
+ virtual status_t prepare(uint32_t radius) = 0;
+ // Render blur to the bound framebuffer (screen).
+ status_t render();
+
+protected:
+ void drawMesh(GLuint uv, GLuint position);
+ string getSimpleFragShader() const;
+ string getVertexShader() const;
+
+ GLESRenderEngine& mEngine;
+ // Frame buffer holding the composited background.
+ GLFramebuffer mCompositionFbo;
+ // Frame buffer holding the blur result.
+ GLFramebuffer mBlurredFbo;
+
+private:
+ bool mTexturesAllocated = false;
+
+ GenericProgram mSimpleProgram;
+ GLuint mSPosLoc;
+ GLuint mSUvLoc;
+ GLuint mSTextureLoc;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
new file mode 100644
index 0000000..b1ad72c
--- /dev/null
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GaussianBlurFilter.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <ui/GraphicTypes.h>
+#include <cstdint>
+
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GaussianBlurFilter::GaussianBlurFilter(GLESRenderEngine& engine)
+ : BlurFilter(engine),
+ mVerticalPassFbo(engine),
+ mVerticalProgram(engine),
+ mHorizontalProgram(engine) {
+ mVerticalProgram.compile(getVertexShader(), getFragmentShader(false));
+ mVPosLoc = mVerticalProgram.getAttributeLocation("aPosition");
+ mVUvLoc = mVerticalProgram.getAttributeLocation("aUV");
+ mVTextureLoc = mVerticalProgram.getUniformLocation("uTexture");
+ mVSizeLoc = mVerticalProgram.getUniformLocation("uSize");
+ mVRadiusLoc = mVerticalProgram.getUniformLocation("uRadius");
+
+ mHorizontalProgram.compile(getVertexShader(), getFragmentShader(true));
+ mHPosLoc = mHorizontalProgram.getAttributeLocation("aPosition");
+ mHUvLoc = mHorizontalProgram.getAttributeLocation("aUV");
+ mHTextureLoc = mHorizontalProgram.getUniformLocation("uTexture");
+ mHSizeLoc = mHorizontalProgram.getUniformLocation("uSize");
+ mHRadiusLoc = mHorizontalProgram.getUniformLocation("uRadius");
+}
+
+void GaussianBlurFilter::allocateTextures() {
+ mVerticalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
+}
+
+status_t GaussianBlurFilter::prepare(uint32_t radius) {
+ ATRACE_NAME("GaussianBlurFilter::prepare");
+
+ if (mVerticalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid vertical FBO");
+ return mVerticalPassFbo.getStatus();
+ }
+ if (!mVerticalProgram.isValid()) {
+ ALOGE("Invalid vertical shader");
+ return GL_INVALID_OPERATION;
+ }
+ if (!mHorizontalProgram.isValid()) {
+ ALOGE("Invalid horizontal shader");
+ return GL_INVALID_OPERATION;
+ }
+
+ // First, we'll apply the vertical pass, that receives the flattened background layers.
+ mVerticalPassFbo.bind();
+ mVerticalProgram.useProgram();
+
+ // set uniforms
+ auto width = mVerticalPassFbo.getBufferWidth();
+ auto height = mVerticalPassFbo.getBufferHeight();
+ glViewport(0, 0, width, height);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+ glUniform1i(mVTextureLoc, 0);
+ glUniform2f(mVSizeLoc, width, height);
+ glUniform1f(mVRadiusLoc, radius * kFboScale);
+ mEngine.checkErrors("Setting vertical-diagonal pass uniforms");
+
+ drawMesh(mVUvLoc, mVPosLoc);
+
+ // Blur vertically on a secondary pass
+ mBlurredFbo.bind();
+ mHorizontalProgram.useProgram();
+
+ // set uniforms
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mVerticalPassFbo.getTextureName());
+ glUniform1i(mHTextureLoc, 0);
+ glUniform2f(mHSizeLoc, width, height);
+ glUniform1f(mHRadiusLoc, radius * kFboScale);
+ mEngine.checkErrors("Setting vertical pass uniforms");
+
+ drawMesh(mHUvLoc, mHPosLoc);
+
+ // reset active texture
+ mBlurredFbo.unbind();
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // unbind program
+ glUseProgram(0);
+
+ return NO_ERROR;
+}
+
+string GaussianBlurFilter::getFragmentShader(bool horizontal) const {
+ string shader = "#version 310 es\n#define DIRECTION ";
+ shader += (horizontal ? "1" : "0");
+ shader += R"SHADER(
+ precision lowp float;
+
+ uniform sampler2D uTexture;
+ uniform vec2 uSize;
+ uniform float uRadius;
+
+ in mediump vec2 vUV;
+
+ out vec4 fragColor;
+
+ #define PI 3.14159265359
+ #define THETA 0.352
+ #define MU 0.0
+ #define A 1.0 / (THETA * sqrt(2.0 * PI))
+ #define K 1.0 / (2.0 * THETA * THETA)
+ #define MAX_SAMPLES 12
+
+ float gaussianBellCurve(float x) {
+ float tmp = (x - MU);
+ return exp(-K * tmp * tmp);
+ }
+
+ vec3 gaussianBlur(sampler2D texture, mediump vec2 uv, float size,
+ vec2 direction, float radius) {
+ float totalWeight = 0.0;
+ vec3 blurred = vec3(0.);
+ int samples = min(int(floor(radius / 2.0)), MAX_SAMPLES);
+ float inc = radius / (size * 2.0);
+
+ for (int i = -samples; i <= samples; i++) {
+ float normalized = (float(i) / float(samples));
+ float weight = gaussianBellCurve(normalized);
+ float radInc = inc * normalized;
+ blurred += weight * (texture(texture, uv + radInc * direction)).rgb;;
+ totalWeight += weight;
+ }
+
+ return blurred / totalWeight;
+ }
+
+ void main() {
+ #if DIRECTION == 1
+ vec3 color = gaussianBlur(uTexture, vUV, uSize.x, vec2(1.0, 0.0), uRadius);
+ #else
+ vec3 color = gaussianBlur(uTexture, vUV, uSize.y, vec2(0.0, 1.0), uRadius);
+ #endif
+ fragColor = vec4(color.r, color.g, color.b, texture(uTexture, vUV).a);
+ }
+
+ )SHADER";
+ return shader;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.h b/libs/renderengine/gl/filters/GaussianBlurFilter.h
new file mode 100644
index 0000000..acf0f07
--- /dev/null
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 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 <ui/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+#include "BlurFilter.h"
+#include "GenericProgram.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GaussianBlurFilter : public BlurFilter {
+public:
+ explicit GaussianBlurFilter(GLESRenderEngine& engine);
+ status_t prepare(uint32_t radius) override;
+ void allocateTextures() override;
+
+private:
+ string getFragmentShader(bool horizontal) const;
+
+ // Initial, vertical render pass
+ GLFramebuffer mVerticalPassFbo;
+
+ // Vertical pass and its uniforms
+ GenericProgram mVerticalProgram;
+ GLuint mVPosLoc;
+ GLuint mVUvLoc;
+ GLuint mVTextureLoc;
+ GLuint mVSizeLoc;
+ GLuint mVRadiusLoc;
+
+ // Horizontal pass and its uniforms
+ GenericProgram mHorizontalProgram;
+ GLuint mHPosLoc;
+ GLuint mHUvLoc;
+ GLuint mHTextureLoc;
+ GLuint mHSizeLoc;
+ GLuint mHRadiusLoc;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/gl/filters/GenericProgram.cpp b/libs/renderengine/gl/filters/GenericProgram.cpp
new file mode 100644
index 0000000..bb35889
--- /dev/null
+++ b/libs/renderengine/gl/filters/GenericProgram.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#include "GenericProgram.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GenericProgram::GenericProgram(GLESRenderEngine& engine) : mEngine(engine) {}
+
+GenericProgram::~GenericProgram() {
+ if (mVertexShaderHandle != 0) {
+ if (mProgramHandle != 0) {
+ glDetachShader(mProgramHandle, mVertexShaderHandle);
+ }
+ glDeleteShader(mVertexShaderHandle);
+ }
+
+ if (mFragmentShaderHandle != 0) {
+ if (mProgramHandle != 0) {
+ glDetachShader(mProgramHandle, mFragmentShaderHandle);
+ }
+ glDeleteShader(mFragmentShaderHandle);
+ }
+
+ if (mProgramHandle != 0) {
+ glDeleteProgram(mProgramHandle);
+ }
+}
+
+void GenericProgram::compile(string vertexShader, string fragmentShader) {
+ mVertexShaderHandle = compileShader(GL_VERTEX_SHADER, vertexShader);
+ mFragmentShaderHandle = compileShader(GL_FRAGMENT_SHADER, fragmentShader);
+ if (mVertexShaderHandle == 0 || mFragmentShaderHandle == 0) {
+ ALOGE("Aborting program creation.");
+ return;
+ }
+ mProgramHandle = createAndLink(mVertexShaderHandle, mFragmentShaderHandle);
+ mEngine.checkErrors("Linking program");
+}
+
+void GenericProgram::useProgram() const {
+ glUseProgram(mProgramHandle);
+}
+
+GLuint GenericProgram::compileShader(GLuint type, string src) const {
+ const GLuint shader = glCreateShader(type);
+ if (shader == 0) {
+ mEngine.checkErrors("Creating shader");
+ return 0;
+ }
+ const GLchar* charSrc = (const GLchar*)src.c_str();
+ glShaderSource(shader, 1, &charSrc, nullptr);
+ glCompileShader(shader);
+
+ GLint isCompiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
+ if (isCompiled == GL_FALSE) {
+ GLint maxLength = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
+ string errorLog;
+ errorLog.reserve(maxLength);
+ glGetShaderInfoLog(shader, maxLength, &maxLength, errorLog.data());
+ glDeleteShader(shader);
+ ALOGE("Error compiling shader: %s", errorLog.c_str());
+ return 0;
+ }
+ return shader;
+}
+GLuint GenericProgram::createAndLink(GLuint vertexShader, GLuint fragmentShader) const {
+ const GLuint program = glCreateProgram();
+ mEngine.checkErrors("Creating program");
+
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, fragmentShader);
+ glLinkProgram(program);
+ mEngine.checkErrors("Linking program");
+ return program;
+}
+
+GLuint GenericProgram::getUniformLocation(const string name) const {
+ if (mProgramHandle == 0) {
+ ALOGE("Can't get location of %s on an invalid program.", name.c_str());
+ return -1;
+ }
+ return glGetUniformLocation(mProgramHandle, (const GLchar*)name.c_str());
+}
+
+GLuint GenericProgram::getAttributeLocation(const string name) const {
+ if (mProgramHandle == 0) {
+ ALOGE("Can't get location of %s on an invalid program.", name.c_str());
+ return -1;
+ }
+ return glGetAttribLocation(mProgramHandle, (const GLchar*)name.c_str());
+}
+
+bool GenericProgram::isValid() const {
+ return mProgramHandle != 0;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/GenericProgram.h b/libs/renderengine/gl/filters/GenericProgram.h
new file mode 100644
index 0000000..6da2a5a
--- /dev/null
+++ b/libs/renderengine/gl/filters/GenericProgram.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 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 <ui/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GenericProgram {
+public:
+ explicit GenericProgram(GLESRenderEngine& renderEngine);
+ ~GenericProgram();
+ void compile(string vertexShader, string fragmentShader);
+ bool isValid() const;
+ void useProgram() const;
+ GLuint getAttributeLocation(const string name) const;
+ GLuint getUniformLocation(const string name) const;
+
+private:
+ GLuint compileShader(GLuint type, const string src) const;
+ GLuint createAndLink(GLuint vertexShader, GLuint fragmentShader) const;
+
+ GLESRenderEngine& mEngine;
+ GLuint mVertexShaderHandle = 0;
+ GLuint mFragmentShaderHandle = 0;
+ GLuint mProgramHandle = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/LensBlurFilter.cpp b/libs/renderengine/gl/filters/LensBlurFilter.cpp
new file mode 100644
index 0000000..386bd91
--- /dev/null
+++ b/libs/renderengine/gl/filters/LensBlurFilter.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "LensBlurFilter.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <ui/GraphicTypes.h>
+#include <cstdint>
+
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+// Number of blur samples in shader (for loop)
+static constexpr auto kNumSamples = 12;
+
+LensBlurFilter::LensBlurFilter(GLESRenderEngine& engine)
+ : BlurFilter(engine),
+ mVerticalDiagonalPassFbo(engine, true /* multiTarget */),
+ mVerticalDiagonalProgram(engine),
+ mCombinedProgram(engine) {
+ mVerticalDiagonalProgram.compile(getVertexShader(), getFragmentShader(false));
+ mCombinedProgram.compile(getVertexShader(), getFragmentShader(true));
+
+ mVDPosLoc = mVerticalDiagonalProgram.getAttributeLocation("aPosition");
+ mVDUvLoc = mVerticalDiagonalProgram.getAttributeLocation("aUV");
+ mVDTexture0Loc = mVerticalDiagonalProgram.getUniformLocation("uTexture0");
+ mVDSizeLoc = mVerticalDiagonalProgram.getUniformLocation("uSize");
+ mVDRadiusLoc = mVerticalDiagonalProgram.getUniformLocation("uRadius");
+ mVDNumSamplesLoc = mVerticalDiagonalProgram.getUniformLocation("uNumSamples");
+
+ mCPosLoc = mCombinedProgram.getAttributeLocation("aPosition");
+ mCUvLoc = mCombinedProgram.getAttributeLocation("aUV");
+ mCTexture0Loc = mCombinedProgram.getUniformLocation("uTexture0");
+ mCTexture1Loc = mCombinedProgram.getUniformLocation("uTexture1");
+ mCSizeLoc = mCombinedProgram.getUniformLocation("uSize");
+ mCRadiusLoc = mCombinedProgram.getUniformLocation("uRadius");
+ mCNumSamplesLoc = mCombinedProgram.getUniformLocation("uNumSamples");
+}
+
+void LensBlurFilter::allocateTextures() {
+ mVerticalDiagonalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(),
+ mBlurredFbo.getBufferHeight());
+}
+
+status_t LensBlurFilter::prepare(uint32_t radius) {
+ ATRACE_NAME("LensBlurFilter::prepare");
+
+ if (mVerticalDiagonalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid vertical-diagonal FBO");
+ return mVerticalDiagonalPassFbo.getStatus();
+ }
+ if (!mVerticalDiagonalProgram.isValid()) {
+ ALOGE("Invalid vertical-diagonal shader");
+ return GL_INVALID_OPERATION;
+ }
+ if (!mCombinedProgram.isValid()) {
+ ALOGE("Invalid blur shader");
+ return GL_INVALID_OPERATION;
+ }
+
+ // First, we'll apply the vertical/diagonal pass, that receives the flattened background layers,
+ // and writes the output to two textures (vertical and diagonal.)
+ mVerticalDiagonalPassFbo.bind();
+ mVerticalDiagonalProgram.useProgram();
+
+ // set uniforms
+ auto width = mVerticalDiagonalPassFbo.getBufferWidth();
+ auto height = mVerticalDiagonalPassFbo.getBufferHeight();
+ glViewport(0, 0, width, height);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+ glUniform1i(mVDTexture0Loc, 0);
+ glUniform2f(mVDSizeLoc, width, height);
+ glUniform1f(mVDRadiusLoc, radius * kFboScale);
+ glUniform1i(mVDNumSamplesLoc, kNumSamples);
+ mEngine.checkErrors("Setting vertical-diagonal pass uniforms");
+
+ drawMesh(mVDUvLoc, mVDPosLoc);
+
+ // Now we'll combine the multi render pass into a blurred image
+ mBlurredFbo.bind();
+ mCombinedProgram.useProgram();
+
+ // set uniforms
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mVerticalDiagonalPassFbo.getTextureName());
+ glUniform1i(mCTexture0Loc, 0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mVerticalDiagonalPassFbo.getSecondaryTextureName());
+ glUniform1i(mCTexture1Loc, 1);
+ glUniform2f(mCSizeLoc, width, height);
+ glUniform1f(mCRadiusLoc, radius * kFboScale);
+ glUniform1i(mCNumSamplesLoc, kNumSamples);
+ mEngine.checkErrors("Setting vertical pass uniforms");
+
+ drawMesh(mCUvLoc, mCPosLoc);
+
+ // reset active texture
+ mBlurredFbo.unbind();
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // unbind program
+ glUseProgram(0);
+
+ return NO_ERROR;
+}
+
+string LensBlurFilter::getFragmentShader(bool forComposition) const {
+ string shader = "#version 310 es\n#define DIRECTION ";
+ shader += (forComposition ? "1" : "0");
+ shader += R"SHADER(
+ precision lowp float;
+
+ #define BOKEH_ANGLE 0.0
+ #define PI 3.14159265359
+
+ uniform sampler2D uTexture0;
+ uniform vec2 uSize;
+ uniform float uRadius;
+ uniform int uNumSamples;
+
+ in mediump vec2 vUV;
+
+ #if DIRECTION == 0
+ layout(location = 0) out vec4 fragColor0;
+ layout(location = 1) out vec4 fragColor1;
+ #else
+ uniform sampler2D uTexture1;
+ out vec4 fragColor;
+ #endif
+
+ vec4 blur(const sampler2D tex, in vec2 uv, const vec2 direction, float radius,
+ in int samples, float intensity) {
+ vec4 finalColor = vec4(vec3(0.0), 1.0);
+ float blurAmount = 0.0;
+ uv += direction * 0.5;
+
+ for (int i = 0; i < samples; i++){
+ float delta = radius * float(i) / float(samples);
+ vec4 color = texture(tex, uv + direction * delta);
+ color.rgb *= intensity;
+ color *= color.a;
+ blurAmount += color.a;
+ finalColor += color;
+ }
+
+ return finalColor / blurAmount;
+ }
+
+ vec4 blur(const sampler2D tex, in vec2 uv, const vec2 direction, float radius,
+ in int samples) {
+ return blur(tex, uv, direction, radius, samples, 1.0);
+ }
+
+ vec4[2] verticalDiagonalLensBlur (vec2 uv, sampler2D texture, vec2 resolution,
+ float radius, int samples) {
+ float coc = texture(texture, uv).a;
+
+ // Vertical Blur
+ vec2 blurDirV = (coc / resolution.xy) * vec2(cos(BOKEH_ANGLE + PI / 2.0),
+ sin(BOKEH_ANGLE + PI / 2.0));
+ vec3 colorV = blur(texture, uv, blurDirV, radius, samples).rgb * coc;
+
+ // Diagonal Blur
+ vec2 blurDirD = (coc / resolution.xy) * vec2(cos(BOKEH_ANGLE - PI / 6.0),
+ sin(BOKEH_ANGLE - PI / 6.0));
+ vec3 colorD = blur(texture, uv, blurDirD, radius, samples).rgb * coc;
+
+ vec4 composed[2];
+ composed[0] = vec4(colorV, coc);
+ // added * 0.5, to remap
+ composed[1] = vec4((colorD + colorV) * 0.5, coc);
+
+ return composed;
+ }
+
+ vec4 rhombiLensBlur (vec2 uv, sampler2D texture0, sampler2D texture1, vec2 resolution,
+ float radius, int samples) {
+ float coc1 = texture(texture0, uv).a;
+ float coc2 = texture(texture1, uv).a;
+
+ vec2 blurDirection1 = coc1 / resolution.xy * vec2(cos(BOKEH_ANGLE - PI / 6.0), sin(BOKEH_ANGLE - PI / 6.0));
+ vec3 color1 = blur(texture0, uv, blurDirection1, radius, samples).rgb * coc1;
+
+ vec2 blurDirection2 = coc2 / resolution.xy * vec2(cos(BOKEH_ANGLE - 5.0 * PI / 6.0), sin(BOKEH_ANGLE - 5.0 * PI / 6.0));
+ vec3 color2 = blur(texture1, uv, blurDirection2, radius, samples, 2.0).rgb * coc2;
+
+ return vec4((color1 + color2) * 0.33, 1.0);
+ }
+
+ void main() {
+ #if DIRECTION == 0
+ // First pass: outputs two textures
+ vec4 colorOut[] = verticalDiagonalLensBlur(vUV, uTexture0, uSize, uRadius, uNumSamples);
+ fragColor0 = colorOut[0];
+ fragColor1 = colorOut[1];
+ #else
+ // Second pass: combines both textures into a blurred one.
+ fragColor = rhombiLensBlur(vUV, uTexture0, uTexture1, uSize, uRadius, uNumSamples);
+ #endif
+ }
+
+ )SHADER";
+ return shader;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/LensBlurFilter.h b/libs/renderengine/gl/filters/LensBlurFilter.h
new file mode 100644
index 0000000..8543f0d
--- /dev/null
+++ b/libs/renderengine/gl/filters/LensBlurFilter.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2019 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 <ui/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+#include "BlurFilter.h"
+#include "GenericProgram.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class LensBlurFilter : public BlurFilter {
+public:
+ explicit LensBlurFilter(GLESRenderEngine& engine);
+ status_t prepare(uint32_t radius) override;
+ void allocateTextures() override;
+
+private:
+ string getFragmentShader(bool forComposition) const;
+
+ // Intermediate render pass
+ GLFramebuffer mVerticalDiagonalPassFbo;
+
+ // Vertical/diagonal pass and its uniforms
+ GenericProgram mVerticalDiagonalProgram;
+ GLuint mVDPosLoc;
+ GLuint mVDUvLoc;
+ GLuint mVDTexture0Loc;
+ GLuint mVDSizeLoc;
+ GLuint mVDRadiusLoc;
+ GLuint mVDNumSamplesLoc;
+
+ // Blur composition pass and its uniforms
+ GenericProgram mCombinedProgram;
+ GLuint mCPosLoc;
+ GLuint mCUvLoc;
+ GLuint mCTexture0Loc;
+ GLuint mCTexture1Loc;
+ GLuint mCSizeLoc;
+ GLuint mCRadiusLoc;
+ GLuint mCNumSamplesLoc;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 5aa3f3b..3dc198f 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -149,6 +149,8 @@
bool disableBlending = false;
ShadowSettings shadow;
+
+ int backgroundBlurRadius = 0;
};
static inline bool operator==(const Buffer& lhs, const Buffer& rhs) {
@@ -182,7 +184,8 @@
return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
- lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow;
+ lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
}
// Defining PrintTo helps with Google Tests.
@@ -243,6 +246,7 @@
PrintTo(settings.sourceDataspace, os);
*os << "\n .colorTransform = " << settings.colorTransform;
*os << "\n .disableBlending = " << settings.disableBlending;
+ *os << "\n .backgroundBlurRadius = " << settings.backgroundBlurRadius;
*os << "\n .shadow = ";
PrintTo(settings.shadow, os);
*os << "\n}";
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 4db5c57..e3c2d84 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -174,6 +174,7 @@
bool useColorManagement;
bool enableProtectedContext;
bool precacheToneMapperShaderOnly;
+ bool supportsBackgroundBlur;
RenderEngine::ContextPriority contextPriority;
struct Builder;
@@ -186,12 +187,14 @@
bool _useColorManagement,
bool _enableProtectedContext,
bool _precacheToneMapperShaderOnly,
+ bool _supportsBackgroundBlur,
RenderEngine::ContextPriority _contextPriority)
: pixelFormat(_pixelFormat)
, imageCacheSize(_imageCacheSize)
, useColorManagement(_useColorManagement)
, enableProtectedContext(_enableProtectedContext)
, precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly)
+ , supportsBackgroundBlur(_supportsBackgroundBlur)
, contextPriority(_contextPriority) {}
RenderEngineCreationArgs() = delete;
};
@@ -219,13 +222,18 @@
this->precacheToneMapperShaderOnly = precacheToneMapperShaderOnly;
return *this;
}
+ Builder& setSupportsBackgroundBlur(bool supportsBackgroundBlur) {
+ this->supportsBackgroundBlur = supportsBackgroundBlur;
+ return *this;
+ }
Builder& setContextPriority(RenderEngine::ContextPriority contextPriority) {
this->contextPriority = contextPriority;
return *this;
}
RenderEngineCreationArgs build() const {
return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
- enableProtectedContext, precacheToneMapperShaderOnly, contextPriority);
+ enableProtectedContext, precacheToneMapperShaderOnly,
+ supportsBackgroundBlur, contextPriority);
}
private:
@@ -235,6 +243,7 @@
bool useColorManagement = true;
bool enableProtectedContext = false;
bool precacheToneMapperShaderOnly = false;
+ bool supportsBackgroundBlur = false;
RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
};
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7700b2e..e676740 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -23,6 +23,7 @@
#include <fstream>
#include <gtest/gtest.h>
+#include <cutils/properties.h>
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
#include <ui/PixelFormat.h>
@@ -44,6 +45,7 @@
.setUseColorManagerment(false)
.setEnableProtectedContext(false)
.setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
.build());
}
@@ -328,6 +330,9 @@
void fillBufferWithRoundedCorners();
template <typename SourceVariant>
+ void fillBufferAndBlurBackground();
+
+ template <typename SourceVariant>
void overlayCorners();
void fillRedBufferTextureTransform();
@@ -706,6 +711,51 @@
}
template <typename SourceVariant>
+void RenderEngineTest::fillBufferAndBlurBackground() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.surface_flinger.supports_background_blur", value, "0");
+ if (!atoi(value)) {
+ // This device doesn't support blurs, no-op.
+ return;
+ }
+
+ auto blurRadius = 50;
+ auto center = DEFAULT_DISPLAY_WIDTH / 2;
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings backgroundLayer;
+ backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ SourceVariant::fillColor(backgroundLayer, 0.0f, 1.0f, 0.0f, this);
+ backgroundLayer.alpha = 1.0f;
+ layers.push_back(backgroundLayer);
+
+ renderengine::LayerSettings leftLayer;
+ leftLayer.geometry.boundaries =
+ Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT).toFloatRect();
+ SourceVariant::fillColor(leftLayer, 1.0f, 0.0f, 0.0f, this);
+ leftLayer.alpha = 1.0f;
+ layers.push_back(leftLayer);
+
+ renderengine::LayerSettings blurLayer;
+ blurLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ blurLayer.backgroundBlurRadius = blurRadius;
+ blurLayer.alpha = 0;
+ layers.push_back(blurLayer);
+
+ invokeDraw(settings, layers, mBuffer);
+
+ expectBufferColor(Rect(center - 1, center - 5, center, center + 5), 150, 150, 0, 255,
+ 50 /* tolerance */);
+ expectBufferColor(Rect(center, center - 5, center + 1, center + 5), 150, 150, 0, 255,
+ 50 /* tolerance */);
+}
+
+template <typename SourceVariant>
void RenderEngineTest::overlayCorners() {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
@@ -1032,6 +1082,10 @@
fillBufferWithRoundedCorners<ColorSourceVariant>();
}
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
+ fillBufferAndBlurBackground<ColorSourceVariant>();
+}
+
TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
overlayCorners<ColorSourceVariant>();
}
@@ -1084,6 +1138,10 @@
fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
+ fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
@@ -1136,6 +1194,10 @@
fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
+ fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index ce7399e..30fdf90 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -256,6 +256,75 @@
return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
}
+static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+ EventEntry* eventEntry,
+ int32_t inputTargetFlags) {
+ if (inputTarget.useDefaultPointerInfo()) {
+ const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
+ return std::make_unique<DispatchEntry>(eventEntry, // increments ref
+ inputTargetFlags, pointerInfo.xOffset,
+ pointerInfo.yOffset, inputTarget.globalScaleFactor,
+ pointerInfo.windowXScale, pointerInfo.windowYScale);
+ }
+
+ ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+
+ PointerCoords pointerCoords[motionEntry.pointerCount];
+
+ // Use the first pointer information to normalize all other pointers. This could be any pointer
+ // as long as all other pointers are normalized to the same value and the final DispatchEntry
+ // uses the offset and scale for the normalized pointer.
+ const PointerInfo& firstPointerInfo =
+ inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()];
+
+ // Iterate through all pointers in the event to normalize against the first.
+ for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
+ const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
+ uint32_t pointerId = uint32_t(pointerProperties.id);
+ const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId];
+
+ // The scale factor is the ratio of the current pointers scale to the normalized scale.
+ float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale;
+ float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale;
+
+ pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
+ // First apply the current pointers offset to set the window at 0,0
+ pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset);
+ // Next scale the coordinates.
+ pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff);
+ // Lastly, offset the coordinates so they're in the normalized pointer's frame.
+ pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset,
+ -firstPointerInfo.yOffset);
+ }
+
+ MotionEntry* combinedMotionEntry =
+ new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId, motionEntry.policyFlags,
+ motionEntry.action, motionEntry.actionButton, motionEntry.flags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, motionEntry.edgeFlags,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+ motionEntry.downTime, motionEntry.pointerCount,
+ motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */,
+ 0 /* yOffset */);
+
+ if (motionEntry.injectionState) {
+ combinedMotionEntry->injectionState = motionEntry.injectionState;
+ combinedMotionEntry->injectionState->refCount += 1;
+ }
+
+ std::unique_ptr<DispatchEntry> dispatchEntry =
+ std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
+ inputTargetFlags, firstPointerInfo.xOffset,
+ firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
+ firstPointerInfo.windowXScale,
+ firstPointerInfo.windowYScale);
+ combinedMotionEntry->release();
+ return dispatchEntry;
+}
+
// --- InputDispatcherThread ---
class InputDispatcher::InputDispatcherThread : public Thread {
@@ -1815,23 +1884,34 @@
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
- sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
- if (inputChannel == nullptr) {
- ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
- return;
- }
+ std::vector<InputTarget>::iterator it =
+ std::find_if(inputTargets.begin(), inputTargets.end(),
+ [&windowHandle](const InputTarget& inputTarget) {
+ return inputTarget.inputChannel->getConnectionToken() ==
+ windowHandle->getToken();
+ });
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- InputTarget target;
- target.inputChannel = inputChannel;
- target.flags = targetFlags;
- target.xOffset = -windowInfo->frameLeft;
- target.yOffset = -windowInfo->frameTop;
- target.globalScaleFactor = windowInfo->globalScaleFactor;
- target.windowXScale = windowInfo->windowXScale;
- target.windowYScale = windowInfo->windowYScale;
- target.pointerIds = pointerIds;
- inputTargets.push_back(target);
+
+ if (it == inputTargets.end()) {
+ InputTarget inputTarget;
+ sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+ if (inputChannel == nullptr) {
+ ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
+ return;
+ }
+ inputTarget.inputChannel = inputChannel;
+ inputTarget.flags = targetFlags;
+ inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
+ inputTargets.push_back(inputTarget);
+ it = inputTargets.end() - 1;
+ }
+
+ ALOG_ASSERT(it->flags == targetFlags);
+ ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
+
+ it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
+ windowInfo->windowXScale, windowInfo->windowYScale);
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
@@ -1854,10 +1934,7 @@
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.xOffset = xOffset;
- target.yOffset = yOffset;
- target.pointerIds.clear();
- target.globalScaleFactor = 1.0f;
+ target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */);
inputTargets.push_back(target);
}
@@ -2089,11 +2166,10 @@
}
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
- "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
- "windowScaleFactor=(%f, %f), pointerIds=0x%x",
- connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset,
- inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale,
- inputTarget.windowYScale, inputTarget.pointerIds.value);
+ "globalScaleFactor=%f, pointerIds=0x%x %s",
+ connection->getInputChannelName().c_str(), inputTarget.flags,
+ inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
+ inputTarget.getPointerInfoString().c_str());
#endif
// Skip this event if the connection status is not normal.
@@ -2187,15 +2263,15 @@
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
std::unique_ptr<DispatchEntry> dispatchEntry =
- std::make_unique<DispatchEntry>(eventEntry, // increments ref
- inputTargetFlags, inputTarget.xOffset,
- inputTarget.yOffset, inputTarget.globalScaleFactor,
- inputTarget.windowXScale, inputTarget.windowYScale);
+ createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);
+ // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
+ // different EventEntry than what was passed in.
+ EventEntry* newEntry = dispatchEntry->eventEntry;
// Apply target flags and update the connection's input state.
- switch (eventEntry->type) {
+ switch (newEntry->type) {
case EventEntry::Type::KEY: {
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*eventEntry);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry);
dispatchEntry->resolvedAction = keyEntry.action;
dispatchEntry->resolvedFlags = keyEntry.flags;
@@ -2211,7 +2287,7 @@
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry);
if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
@@ -2265,14 +2341,14 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events should not go to apps",
- EventEntry::typeToString(eventEntry->type));
+ EventEntry::typeToString(newEntry->type));
break;
}
}
// Remember that we are waiting for this dispatch to complete.
if (dispatchEntry->hasForegroundTarget()) {
- incrementPendingForegroundDispatches(eventEntry);
+ incrementPendingForegroundDispatches(newEntry);
}
// Enqueue the dispatch entry.
@@ -2661,15 +2737,9 @@
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.xOffset = -windowInfo->frameLeft;
- target.yOffset = -windowInfo->frameTop;
+ target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+ windowInfo->windowXScale, windowInfo->windowYScale);
target.globalScaleFactor = windowInfo->globalScaleFactor;
- target.windowXScale = windowInfo->windowXScale;
- target.windowYScale = windowInfo->windowYScale;
- } else {
- target.xOffset = 0;
- target.yOffset = 0;
- target.globalScaleFactor = 1.0f;
}
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 80fa2cb..0588374 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -42,4 +42,63 @@
return StringPrintf("%" PRId32, dispatchMode);
}
+void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset,
+ float windowXScale, float windowYScale) {
+ // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
+ // and non splittable windows since we will just use all the pointers from the input event.
+ if (newPointerIds.isEmpty()) {
+ setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale);
+ return;
+ }
+
+ // Ensure that the new set of pointers doesn't overlap with the current set of pointers.
+ ALOG_ASSERT((pointerIds & newPointerIds) == 0);
+
+ pointerIds |= newPointerIds;
+ while (!newPointerIds.isEmpty()) {
+ int32_t pointerId = newPointerIds.clearFirstMarkedBit();
+ pointerInfos[pointerId].xOffset = xOffset;
+ pointerInfos[pointerId].yOffset = yOffset;
+ pointerInfos[pointerId].windowXScale = windowXScale;
+ pointerInfos[pointerId].windowYScale = windowYScale;
+ }
+}
+
+void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+ float windowYScale) {
+ pointerIds.clear();
+ pointerInfos[0].xOffset = xOffset;
+ pointerInfos[0].yOffset = yOffset;
+ pointerInfos[0].windowXScale = windowXScale;
+ pointerInfos[0].windowYScale = windowYScale;
+}
+
+bool InputTarget::useDefaultPointerInfo() const {
+ return pointerIds.isEmpty();
+}
+
+const PointerInfo& InputTarget::getDefaultPointerInfo() const {
+ return pointerInfos[0];
+}
+
+std::string InputTarget::getPointerInfoString() const {
+ if (useDefaultPointerInfo()) {
+ const PointerInfo& pointerInfo = getDefaultPointerInfo();
+ return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)",
+ pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale,
+ pointerInfo.windowYScale);
+ }
+
+ std::string out;
+ for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) {
+ if (!pointerIds.hasBit(i)) {
+ continue;
+ }
+ out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f "
+ "windowScaleFactor=(%.1f, %.1f)",
+ i, pointerInfos[i].xOffset, pointerInfos[i].yOffset,
+ pointerInfos[i].windowXScale, pointerInfos[i].windowYScale);
+ }
+ return out;
+}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 1ba5eff..499a75f 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -24,6 +24,22 @@
namespace android::inputdispatcher {
/*
+ * Information about each pointer for an InputTarget. This includes offset and scale so
+ * all pointers can be normalized to a single offset and scale.
+ *
+ * These values are ignored for KeyEvents
+ */
+struct PointerInfo {
+ // The x and y offset to add to a MotionEvent as it is delivered.
+ float xOffset = 0.0f;
+ float yOffset = 0.0f;
+
+ // Scaling factor to apply to MotionEvent as it is delivered.
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
+};
+
+/*
* An input target specifies how an input event is to be dispatched to a particular window
* including the window's input channel, control flags, a timeout, and an X / Y offset to
* be added to input event coordinates to compensate for the absolute position of the
@@ -95,20 +111,37 @@
// Flags for the input target.
int32_t flags = 0;
- // The x and y offset to add to a MotionEvent as it is delivered.
- // (ignored for KeyEvents)
- float xOffset = 0.0f;
- float yOffset = 0.0f;
-
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
BitSet32 pointerIds;
+ // The data is stored by the pointerId. Use the bit position of pointerIds to look up
+ // PointerInfo per pointerId.
+ PointerInfo pointerInfos[MAX_POINTERS];
+
+ void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale,
+ float windowYScale);
+ void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+ float windowYScale);
+
+ /**
+ * Returns whether the default pointer information should be used. This will be true when the
+ * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors
+ * and non splittable windows since we want all pointers for the EventEntry to go to this
+ * target.
+ */
+ bool useDefaultPointerInfo() const;
+
+ /**
+ * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is
+ * true.
+ */
+ const PointerInfo& getDefaultPointerInfo() const;
+
+ std::string getPointerInfoString() const;
};
std::string dispatchModeToString(int32_t dispatchMode);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index c262abb..98ebf50 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1571,4 +1571,107 @@
consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
}
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) {
+ mWindow2->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) {
+ mWindow2->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+ // Move both windows
+ touchedPoints = {{20, 20}, {175, 175}};
+ expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+ getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
+ mWindow1->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+ // Move both windows
+ touchedPoints = {{20, 20}, {175, 175}};
+ expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+ getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+}
+
} // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index c173c66..287fe89 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -239,6 +239,8 @@
mReleasePreviousBuffer = true;
}
+ mFrameCounter++;
+
mCurrentState.buffer = buffer;
mCurrentState.clientCacheId = clientCacheId;
mCurrentState.modified = true;
@@ -496,6 +498,8 @@
handle->latchTime = latchTime;
}
+ mFrameNumber = mFrameCounter;
+
if (!SyncFeatures::getInstance().useNativeFenceSync()) {
// Bind the new buffer to the GL texture.
//
@@ -557,8 +561,6 @@
compositionState.buffer = mBufferInfo.mBuffer;
compositionState.bufferSlot = mBufferInfo.mBufferSlot;
compositionState.acquireFence = mBufferInfo.mFence;
-
- mFrameNumber++;
}
void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 574bc51..9427283 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -141,7 +141,8 @@
std::atomic<bool> mSidebandStreamChanged{false};
- mutable uint32_t mFrameNumber{0};
+ mutable uint64_t mFrameNumber{0};
+ uint64_t mFrameCounter{0};
sp<Fence> mPreviousReleaseFence;
uint64_t mPreviousBufferId = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index a64fdbf..3dbd25e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -63,6 +63,9 @@
// The alpha value for this layer
float alpha{1.f};
+ // Background blur in pixels
+ int backgroundBlurRadius{0};
+
// The transform from layer local coordinates to composition coordinates
ui::Transform geomLayerTransform;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 076fdad..a280c75 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -162,7 +162,7 @@
virtual void setCompositionEnabled(bool) = 0;
// Sets the projection state to use
- virtual void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+ virtual void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
const Rect& viewport, const Rect& scissor, bool needsFiltering) = 0;
// Sets the bounds to use
virtual void setBounds(const ui::Size&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 159e928..e2d01ed 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -36,7 +36,7 @@
bool isValid() const override;
std::optional<DisplayId> getDisplayId() const override;
void setCompositionEnabled(bool) override;
- void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+ void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
void setBounds(const ui::Size&) override;
void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
@@ -116,6 +116,7 @@
private:
void dirtyEntireOutput();
+ compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
compositionengine::Output::ColorProfile pickColorProfile(
const compositionengine::CompositionRefreshArgs&) const;
@@ -126,6 +127,7 @@
std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
ReleasedLayers mReleasedLayers;
+ OutputLayer* mLayerRequestingBackgroundBlur = nullptr;
};
// This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 7f5bd06..367e8ca 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -38,7 +38,7 @@
MOCK_METHOD1(setCompositionEnabled, void(bool));
MOCK_METHOD6(setProjection,
- void(const ui::Transform&, int32_t, const Rect&, const Rect&, const Rect&, bool));
+ void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&, bool));
MOCK_METHOD1(setBounds, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index 995a0ca..1e7c97c 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -35,7 +35,7 @@
slot >= BufferQueue::NUM_BUFFER_SLOTS) {
*outSlot = 0;
} else {
- *outSlot = slot;
+ *outSlot = static_cast<uint32_t>(slot);
}
auto& currentBuffer = mBuffers[*outSlot];
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 085e838..8065e65 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -61,6 +61,7 @@
out.append("\n ");
dumpVal(out, "blend", toString(blendMode), blendMode);
dumpVal(out, "alpha", alpha);
+ dumpVal(out, "backgroundBlurRadius", backgroundBlurRadius);
out.append("\n ");
dumpVal(out, "type", type);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 01413b9..bffb74f 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -101,7 +101,7 @@
dirtyEntireOutput();
}
-void Output::setProjection(const ui::Transform& transform, int32_t orientation, const Rect& frame,
+void Output::setProjection(const ui::Transform& transform, uint32_t orientation, const Rect& frame,
const Rect& viewport, const Rect& scissor, bool needsFiltering) {
auto& outputState = editState();
outputState.transform = transform;
@@ -433,7 +433,7 @@
if (layerFEState.shadowRadius > 0.0f) {
// if the layer casts a shadow, offset the layers visible region and
// calculate the shadow region.
- const int32_t inset = layerFEState.shadowRadius * -1.0f;
+ const auto inset = static_cast<int32_t>(ceilf(layerFEState.shadowRadius) * -1.0f);
Rect visibleRectWithShadows(visibleRect);
visibleRectWithShadows.inset(inset, inset, inset, inset);
visibleRegion.set(visibleRectWithShadows);
@@ -457,7 +457,7 @@
}
// compute the opaque region
- const int32_t layerOrientation = tr.getOrientation();
+ const auto layerOrientation = tr.getOrientation();
if (layerFEState.isOpaque && ((layerOrientation & ui::Transform::ROT_INVALID) == 0)) {
// If we one of the simple category of transforms (0/90/180/270 rotation
// + any flip), then the opaque region is the layer's footprint.
@@ -578,15 +578,33 @@
return;
}
+ mLayerRequestingBackgroundBlur = findLayerRequestingBackgroundComposition();
+ bool forceClientComposition = mLayerRequestingBackgroundBlur != nullptr;
+
for (auto* layer : getOutputLayersOrderedByZ()) {
layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame,
- refreshArgs.devOptForceClientComposition);
+ refreshArgs.devOptForceClientComposition ||
+ forceClientComposition);
+
+ if (mLayerRequestingBackgroundBlur == layer) {
+ forceClientComposition = false;
+ }
// Send the updated state to the HWC, if appropriate.
layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);
}
}
+compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const {
+ compositionengine::OutputLayer* layerRequestingBgComposition = nullptr;
+ for (auto* layer : getOutputLayersOrderedByZ()) {
+ if (layer->getLayer().getFEState().backgroundBlurRadius > 0) {
+ layerRequestingBgComposition = layer;
+ }
+ }
+ return layerRequestingBgComposition;
+}
+
void Output::updateColorProfile(const compositionengine::CompositionRefreshArgs& refreshArgs) {
setColorProfile(pickColorProfile(refreshArgs));
}
@@ -854,11 +872,12 @@
}
// We boost GPU frequency here because there will be color spaces conversion
- // and it's expensive. We boost the GPU frequency so that GPU composition can
- // finish in time. We must reset GPU frequency afterwards, because high frequency
- // consumes extra battery.
+ // or complex GPU shaders and it's expensive. We boost the GPU frequency so that
+ // GPU composition can finish in time. We must reset GPU frequency afterwards,
+ // because high frequency consumes extra battery.
const bool expensiveRenderingExpected =
- clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3;
+ clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3 ||
+ mLayerRequestingBackgroundBlur != nullptr;
if (expensiveRenderingExpected) {
setExpensiveRenderingExpected(true);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index ac66d8c..914a047 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -392,7 +392,8 @@
outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
}
- if (auto error = hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId);
+ if (auto error = hwcLayer->setInfo(static_cast<uint32_t>(outputIndependentState.type),
+ static_cast<uint32_t>(outputIndependentState.appId));
error != HWC2::Error::None) {
ALOGE("[%s] Failed to set info %s (%d)", getLayerFE().getDebugName(),
to_string(error).c_str(), static_cast<int32_t>(error));
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 10512eb..e981172 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -91,7 +91,8 @@
}
void RenderSurface::setDisplaySize(const ui::Size& size) {
- mDisplaySurface->resizeBuffers(size.width, size.height);
+ mDisplaySurface->resizeBuffers(static_cast<uint32_t>(size.width),
+ static_cast<uint32_t>(size.height));
mSize = size;
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index ebcd0a0..bcaa529 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -139,14 +139,14 @@
struct CompositionEngineUpdateCursorAsyncTest : public CompositionEngineTest {
public:
CompositionEngineUpdateCursorAsyncTest() {
- EXPECT_CALL(*mOutput1, getOutputLayerCount()).WillRepeatedly(Return(0));
+ EXPECT_CALL(*mOutput1, getOutputLayerCount()).WillRepeatedly(Return(0u));
EXPECT_CALL(*mOutput1, getOutputLayerOrderedByZByIndex(_)).Times(0);
- EXPECT_CALL(*mOutput2, getOutputLayerCount()).WillRepeatedly(Return(1));
+ EXPECT_CALL(*mOutput2, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(*mOutput2, getOutputLayerOrderedByZByIndex(0))
.WillRepeatedly(Return(&mOutput2OutputLayer1));
- EXPECT_CALL(*mOutput3, getOutputLayerCount()).WillRepeatedly(Return(2));
+ EXPECT_CALL(*mOutput3, getOutputLayerCount()).WillRepeatedly(Return(2u));
EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(0))
.WillRepeatedly(Return(&mOutput3OutputLayer1));
EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(1))
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 24311c7..730cac8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -64,8 +64,8 @@
constexpr auto TR_ROT_90 = HAL_TRANSFORM_ROT_90;
const mat4 kIdentity;
-const mat4 kNonIdentityHalf = mat4() * 0.5;
-const mat4 kNonIdentityQuarter = mat4() * 0.25;
+const mat4 kNonIdentityHalf = mat4() * 0.5f;
+const mat4 kNonIdentityQuarter = mat4() * 0.25f;
constexpr OutputColorSetting kVendorSpecifiedOutputColorSetting =
static_cast<OutputColorSetting>(0x100);
@@ -138,6 +138,10 @@
EXPECT_CALL(mLayer1, editFEState()).WillRepeatedly(ReturnRef(mLayer1FEState));
EXPECT_CALL(mLayer2, editFEState()).WillRepeatedly(ReturnRef(mLayer2FEState));
EXPECT_CALL(mLayer3, editFEState()).WillRepeatedly(ReturnRef(mLayer3FEState));
+
+ EXPECT_CALL(mLayer1, getFEState()).WillRepeatedly(ReturnRef(mLayer1FEState));
+ EXPECT_CALL(mLayer2, getFEState()).WillRepeatedly(ReturnRef(mLayer2FEState));
+ EXPECT_CALL(mLayer3, getFEState()).WillRepeatedly(ReturnRef(mLayer3FEState));
}
void injectLayer(std::unique_ptr<mock::OutputLayer> layer) {
@@ -984,7 +988,7 @@
};
OutputCollectVisibleLayersTest() {
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
.WillRepeatedly(Return(&mLayer1.outputLayer));
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
@@ -1008,7 +1012,7 @@
TEST_F(OutputCollectVisibleLayersTest, doesMinimalWorkIfNoLayers) {
mRefreshArgs.layers.clear();
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
EXPECT_CALL(mOutput, finalizePendingOutputLayers());
@@ -1058,7 +1062,7 @@
EXPECT_CALL(*mLayer, editFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillRepeatedly(Return(true));
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
.WillRepeatedly(Return(&mOutputLayer));
@@ -1623,7 +1627,7 @@
// When the outputColorSetting is set to kUnmanaged, the implementation sets
// a simple default color profile without looking at anything else.
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
EXPECT_CALL(mOutput,
setColorProfile(ColorProfileEq(
ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
@@ -1638,7 +1642,7 @@
struct OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile
: public OutputUpdateColorProfileTest {
OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile() {
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
}
@@ -1686,7 +1690,7 @@
struct OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile
: public OutputUpdateColorProfileTest {
OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile() {
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
EXPECT_CALL(*mDisplayColorProfile,
getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _, _))
.WillRepeatedly(DoAll(SetArgPointee<2>(ui::Dataspace::UNKNOWN),
@@ -1742,7 +1746,7 @@
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
}
@@ -1864,7 +1868,7 @@
mLayer1.mLayerFEState.dataspace = ui::Dataspace::DISPLAY_BT2020;
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
}
@@ -1917,7 +1921,7 @@
OutputUpdateColorProfileTest_Hdr() {
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
}
@@ -2197,7 +2201,7 @@
mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
mLayer1.mLayerFEState.dataspace = ui::Dataspace::BT2020_PQ;
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
EXPECT_CALL(*mDisplayColorProfile, hasLegacyHdrSupport(ui::Dataspace::BT2020_PQ))
.WillRepeatedly(Return(false));
@@ -2838,7 +2842,7 @@
const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
const Rect OutputComposeSurfacesTest::kDefaultOutputScissor{1009, 1010, 1011, 1012};
-const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5};
+const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
const HdrCapabilities OutputComposeSurfacesTest::
kHdrCapabilities{{},
@@ -3412,7 +3416,7 @@
EXPECT_EQ(mLayers[1].mRELayerSettings.geometry.boundaries, requests[0].geometry.boundaries);
EXPECT_FALSE(requests[0].source.buffer.buffer);
EXPECT_EQ((half3{0.f, 0.f, 0.f}), requests[0].source.solidColor);
- EXPECT_EQ(half{0.f}, requests[0].alpha);
+ EXPECT_EQ(0.f, static_cast<float>(requests[0].alpha));
EXPECT_EQ(true, requests[0].disableBlending);
EXPECT_EQ(mLayers[2].mRELayerSettings, requests[1]);
@@ -3637,6 +3641,29 @@
kDisplayDataspace));
}
+TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) {
+ // Layer requesting blur, or below, should request client composition.
+ EXPECT_CALL(*mOutputLayer1, updateCompositionState(false, true));
+ EXPECT_CALL(*mOutputLayer1, writeStateToHWC(false));
+ EXPECT_CALL(*mOutputLayer2, updateCompositionState(false, true));
+ EXPECT_CALL(*mOutputLayer2, writeStateToHWC(false));
+ EXPECT_CALL(*mOutputLayer3, updateCompositionState(false, false));
+ EXPECT_CALL(*mOutputLayer3, writeStateToHWC(false));
+
+ mLayer2FEState.backgroundBlurRadius = 10;
+
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ 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/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9c4a784..f4d4329 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -109,6 +109,7 @@
mCurrentState.hdrMetadata.validTypes = 0;
mCurrentState.surfaceDamageRegion = Region::INVALID_REGION;
mCurrentState.cornerRadius = 0.0f;
+ mCurrentState.backgroundBlurRadius = 0;
mCurrentState.api = -1;
mCurrentState.hasColorTransform = false;
mCurrentState.colorSpaceAgnostic = false;
@@ -448,6 +449,7 @@
compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
compositionState.alpha = alpha;
+ compositionState.backgroundBlurRadius = drawingState.backgroundBlurRadius;
}
void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const {
@@ -575,6 +577,7 @@
layerSettings.alpha = alpha;
layerSettings.sourceDataspace = getDataSpace();
+ layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
return layerSettings;
}
@@ -1103,6 +1106,16 @@
return true;
}
+bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) {
+ if (mCurrentState.backgroundBlurRadius == backgroundBlurRadius) return false;
+
+ mCurrentState.sequence++;
+ mCurrentState.backgroundBlurRadius = backgroundBlurRadius;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
bool allowNonRectPreservingTransforms) {
ui::Transform t;
@@ -1873,6 +1886,10 @@
return half4(color.r, color.g, color.b, getAlpha());
}
+int32_t Layer::getBackgroundBlurRadius() const {
+ return getDrawingState().backgroundBlurRadius;
+}
+
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 0253098..ffe004f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -179,6 +179,7 @@
half4 color;
float cornerRadius;
+ int backgroundBlurRadius;
bool inputInfoChanged;
InputWindowInfo inputInfo;
@@ -299,6 +300,9 @@
// The shape of the rounded corner rectangle is specified by the crop rectangle of the layer
// from which we inferred the rounded corner radius.
virtual bool setCornerRadius(float cornerRadius);
+ // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which
+ // is specified in pixels.
+ virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
virtual bool setTransparentRegionHint(const Region& transparent);
virtual bool setFlags(uint8_t flags, uint8_t mask);
virtual bool setLayerStack(uint32_t layerStack);
@@ -663,6 +667,7 @@
// down the hierarchy).
half getAlpha() const;
half4 getColor() const;
+ int32_t getBackgroundBlurRadius() const;
// Returns how rounded corners should be drawn for this layer.
// This will traverse the hierarchy until it reaches its root, finding topmost rounded
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index cee5f45..ac32633 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -43,7 +43,7 @@
kMinimumSamplesForPrediction(minimumSamplesForPrediction),
kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
mIdealPeriod(idealPeriod) {
- mRateMap[mIdealPeriod] = {idealPeriod, 0};
+ resetModel();
}
inline void VSyncPredictor::traceInt64If(const char* name, int64_t value) const {
@@ -220,6 +220,10 @@
mRateMap[mIdealPeriod] = {period, 0};
}
+ clearTimestamps();
+}
+
+void VSyncPredictor::clearTimestamps() {
if (!timestamps.empty()) {
mKnownTimestamp = *std::max_element(timestamps.begin(), timestamps.end());
timestamps.clear();
@@ -244,6 +248,12 @@
return needsMoreSamples;
}
+void VSyncPredictor::resetModel() {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+ clearTimestamps();
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 4467c71..e366555 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -41,6 +41,7 @@
void addVsyncTimestamp(nsecs_t timestamp) final;
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
nsecs_t currentPeriod() const final;
+ void resetModel() final;
/*
* Inform the model that the period is anticipated to change to a new value.
@@ -49,7 +50,7 @@
*
* \param [in] period The new period that should be used.
*/
- void setPeriod(nsecs_t period);
+ void setPeriod(nsecs_t period) final;
/* Query if the model is in need of more samples to make a prediction at timePoint.
* \param [in] timePoint The timePoint to inquire of.
@@ -62,6 +63,7 @@
private:
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
+ void clearTimestamps() REQUIRES(mMutex);
inline void traceInt64If(const char* name, int64_t value) const;
bool const mTraceOn;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 20b6238..53fa212 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -14,11 +14,13 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
#define LOG_TAG "VSyncReactor"
//#define LOG_NDEBUG 0
#include "VSyncReactor.h"
#include <log/log.h>
+#include <utils/Trace.h>
#include "TimeKeeper.h"
#include "VSyncDispatch.h"
#include "VSyncTracker.h"
@@ -186,6 +188,7 @@
}
void VSyncReactor::setPeriod(nsecs_t period) {
+ ATRACE_INT64("VSR-setPeriod", period);
std::lock_guard lk(mMutex);
mLastHwVsync.reset();
if (period == getPeriod()) {
@@ -199,7 +202,9 @@
return mTracker->currentPeriod();
}
-void VSyncReactor::beginResync() {}
+void VSyncReactor::beginResync() {
+ mTracker->resetModel();
+}
void VSyncReactor::endResync() {}
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 6be63fe..2b27884 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -61,6 +61,9 @@
*/
virtual void setPeriod(nsecs_t period) = 0;
+ /* Inform the tracker that the samples it has are not accurate for prediction. */
+ virtual void resetModel() = 0;
+
protected:
VSyncTracker(VSyncTracker const&) = delete;
VSyncTracker& operator=(VSyncTracker const&) = delete;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1796476..6d18922 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -338,6 +338,14 @@
mLayerTripleBufferingDisabled = atoi(value);
ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
+ property_get("ro.surface_flinger.supports_background_blur", value, "0");
+ bool supportsBlurs = atoi(value);
+ property_get("debug.sf.disableBlurs", value, "0");
+ bool disableBlurs = atoi(value);
+ mEnableBlurs = supportsBlurs && !disableBlurs;
+ ALOGI_IF(!mEnableBlurs, "Disabling blur effects. supported: %d, disabled: %d", supportsBlurs,
+ disableBlurs);
+
const size_t defaultListSize = MAX_LAYERS;
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
@@ -494,6 +502,7 @@
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
mFrameTracer->initialize();
+ mTimeStats->onBootFinished();
// wait patiently for the window manager death
const String16 name("window");
@@ -580,6 +589,7 @@
.setUseColorManagerment(useColorManagement)
.setEnableProtectedContext(enable_protected_contents(false))
.setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(mEnableBlurs)
.setContextPriority(useContextPriority
? renderengine::RenderEngine::ContextPriority::HIGH
: renderengine::RenderEngine::ContextPriority::MEDIUM)
@@ -875,7 +885,7 @@
repaintEverythingForHWC();
// Start receiving vsync samples now, so that we can detect a period
// switch.
- mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ mScheduler->resyncToHardwareVsync(true, refreshRate.vsyncPeriod);
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// DispSync model is locked.
mVSyncModulator->onRefreshRateChangeInitiated();
@@ -952,9 +962,9 @@
mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
mDesiredActiveConfigChanged = false;
- mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
- auto refreshRate =
+ auto const refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
+ mScheduler->resyncToHardwareVsync(true, refreshRate.vsyncPeriod);
mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
}
@@ -3379,6 +3389,9 @@
if (layer->setCornerRadius(s.cornerRadius))
flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eBackgroundBlurRadiusChanged) {
+ if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) 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/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e839348..7f7d8da 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1025,6 +1025,7 @@
const std::shared_ptr<TimeStats> mTimeStats;
const std::unique_ptr<FrameTracer> mFrameTracer;
bool mUseHwcVirtualDisplays = false;
+ bool mEnableBlurs = false;
std::atomic<uint32_t> mFrameMissedCount = 0;
std::atomic<uint32_t> mHwcFrameMissedCount = 0;
std::atomic<uint32_t> mGpuFrameMissedCount = 0;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 79123f9..6884b4c 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -114,6 +114,7 @@
addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
+ addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius);
if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
addDeferTransactionLocked(transaction, layerId,
layer->mCurrentState.barrierLayer_legacy.promote(),
@@ -322,6 +323,13 @@
cornerRadiusChange->set_corner_radius(cornerRadius);
}
+void SurfaceInterceptor::addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
+ int32_t backgroundBlurRadius) {
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ BackgroundBlurRadiusChange* blurRadiusChange(change->mutable_background_blur_radius());
+ blurRadiusChange->set_background_blur_radius(backgroundBlurRadius);
+}
+
void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
const sp<const Layer>& layer, uint64_t frameNumber)
{
@@ -422,6 +430,9 @@
if (state.what & layer_state_t::eCornerRadiusChanged) {
addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
}
+ if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) {
+ addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius);
+ }
if (state.what & layer_state_t::eDeferTransaction_legacy) {
sp<Layer> otherLayer = nullptr;
if (state.barrierHandle_legacy != nullptr) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index c6f9e8a..a665f62 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -152,6 +152,8 @@
void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
+ void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
+ int32_t backgroundBlurRadius);
void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
const sp<const Layer>& layer, uint64_t frameNumber);
void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index 20c2218..d27fbb4 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -8,12 +8,20 @@
"libcutils",
"liblog",
"libprotobuf-cpp-lite",
+ "libprotoutil",
+ "libstatslog",
+ "libstatspull",
+ "libstatssocket",
"libtimestats_proto",
"libui",
"libutils",
],
export_include_dirs: ["."],
export_shared_lib_headers: [
+ "libprotoutil",
+ "libstatslog",
+ "libstatspull",
+ "libstatssocket",
"libtimestats_proto",
],
cppflags: [
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 0939fa4..7c824ec 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -24,6 +24,7 @@
#include "TimeStats.h"
#include <android-base/stringprintf.h>
+#include <android/util/ProtoOutputStream.h>
#include <log/log.h>
#include <utils/String8.h>
#include <utils/Timers.h>
@@ -36,11 +37,142 @@
namespace impl {
-TimeStats::TimeStats() {
+status_pull_atom_return_t TimeStats::pullAtomCallback(int32_t atom_tag,
+ pulled_stats_event_list* data, void* cookie) {
+ impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie);
+ if (atom_tag == android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) {
+ return timeStats->populateGlobalAtom(data);
+ } else if (atom_tag == android::util::SURFACEFLINGER_STATS_LAYER_INFO) {
+ return timeStats->populateLayerAtom(data);
+ }
+
+ return STATS_PULL_SKIP;
+}
+
+status_pull_atom_return_t TimeStats::populateGlobalAtom(pulled_stats_event_list* data) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (mTimeStats.statsStart == 0) {
+ return STATS_PULL_SKIP;
+ }
+ flushPowerTimeLocked();
+
+ struct stats_event* event = mStatsDelegate->addStatsEventToPullData(data);
+ mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+ mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFrames);
+ mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFrames);
+ mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFrames);
+ mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTime);
+ mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresent.totalTime());
+ mStatsDelegate->statsEventBuild(event);
+ clearGlobalLocked();
+
+ return STATS_PULL_SUCCESS;
+}
+
+namespace {
+// Histograms align with the order of fields in SurfaceflingerStatsLayerInfo.
+const std::array<std::string, 6> kHistogramNames = {
+ "present2present", "post2present", "acquire2present",
+ "latch2present", "desired2present", "post2acquire",
+};
+
+std::string histogramToProtoByteString(const std::unordered_map<int32_t, int32_t>& histogram,
+ size_t maxPulledHistogramBuckets) {
+ auto buckets = std::vector<std::pair<int32_t, int32_t>>(histogram.begin(), histogram.end());
+ std::sort(buckets.begin(), buckets.end(),
+ [](std::pair<int32_t, int32_t>& left, std::pair<int32_t, int32_t>& right) {
+ return left.second > right.second;
+ });
+
+ util::ProtoOutputStream proto;
+ int histogramSize = 0;
+ for (const auto& bucket : buckets) {
+ if (++histogramSize > maxPulledHistogramBuckets) {
+ break;
+ }
+ proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
+ 1 /* field id */,
+ (int32_t)bucket.first);
+ proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+ 2 /* field id */,
+ (int64_t)bucket.second);
+ }
+
+ std::string byteString;
+ proto.serializeToString(&byteString);
+ return byteString;
+}
+} // namespace
+
+status_pull_atom_return_t TimeStats::populateLayerAtom(pulled_stats_event_list* data) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ std::vector<TimeStatsHelper::TimeStatsLayer const*> dumpStats;
+ for (const auto& ele : mTimeStats.stats) {
+ dumpStats.push_back(&ele.second);
+ }
+
+ std::sort(dumpStats.begin(), dumpStats.end(),
+ [](TimeStatsHelper::TimeStatsLayer const* l,
+ TimeStatsHelper::TimeStatsLayer const* r) {
+ return l->totalFrames > r->totalFrames;
+ });
+
+ if (mMaxPulledLayers < dumpStats.size()) {
+ dumpStats.resize(mMaxPulledLayers);
+ }
+
+ for (const auto& layer : dumpStats) {
+ struct stats_event* event = mStatsDelegate->addStatsEventToPullData(data);
+ mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+ mStatsDelegate->statsEventWriteString8(event, layer->layerName.c_str());
+ mStatsDelegate->statsEventWriteInt64(event, layer->totalFrames);
+ mStatsDelegate->statsEventWriteInt64(event, layer->droppedFrames);
+
+ for (const auto& name : kHistogramNames) {
+ const auto& histogram = layer->deltas.find(name);
+ if (histogram == layer->deltas.cend()) {
+ mStatsDelegate->statsEventWriteByteArray(event, nullptr, 0);
+ } else {
+ std::string bytes = histogramToProtoByteString(histogram->second.hist,
+ mMaxPulledHistogramBuckets);
+ mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)bytes.c_str(),
+ bytes.size());
+ }
+ }
+
+ mStatsDelegate->statsEventBuild(event);
+ }
+ clearLayersLocked();
+
+ return STATS_PULL_SUCCESS;
+}
+
+TimeStats::TimeStats() : TimeStats(nullptr, std::nullopt, std::nullopt) {}
+
+TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
+ std::optional<size_t> maxPulledLayers,
+ std::optional<size_t> maxPulledHistogramBuckets) {
+ if (statsDelegate != nullptr) {
+ mStatsDelegate = std::move(statsDelegate);
+ }
+
+ if (maxPulledLayers) {
+ mMaxPulledLayers = *maxPulledLayers;
+ }
+
+ if (maxPulledHistogramBuckets) {
+ mMaxPulledHistogramBuckets = *maxPulledHistogramBuckets;
+ }
+}
+
+void TimeStats::onBootFinished() {
// Temporarily enable TimeStats by default. Telemetry is disabled while
// we move onto statsd, so TimeStats is currently not exercised at all
- // during testing.
- // TODO: remove this.
+ // during testing without enabling by default.
+ // TODO: remove this, as we should only be paying this overhead on devices
+ // where statsd exists.
enable();
}
@@ -69,7 +201,7 @@
}
if (argsMap.count("-clear")) {
- clear();
+ clearAll();
}
if (argsMap.count("-enable")) {
@@ -594,6 +726,10 @@
mEnabled.store(true);
mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
mPowerTime.prevTime = systemTime();
+ mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ TimeStats::pullAtomCallback, nullptr, this);
+ mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ TimeStats::pullAtomCallback, nullptr, this);
ALOGD("Enabled");
}
@@ -606,15 +742,21 @@
flushPowerTimeLocked();
mEnabled.store(false);
mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ mStatsDelegate->unregisterStatsPullAtomCallback(
+ android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+ mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
ALOGD("Disabled");
}
-void TimeStats::clear() {
+void TimeStats::clearAll() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ clearGlobalLocked();
+ clearLayersLocked();
+}
+
+void TimeStats::clearGlobalLocked() {
ATRACE_CALL();
- std::lock_guard<std::mutex> lock(mMutex);
- mTimeStatsTracker.clear();
- mTimeStats.stats.clear();
mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
mTimeStats.statsEnd = 0;
mTimeStats.totalFrames = 0;
@@ -628,7 +770,15 @@
mPowerTime.prevTime = systemTime();
mGlobalRecord.prevPresentTime = 0;
mGlobalRecord.presentFences.clear();
- ALOGD("Cleared");
+ ALOGD("Cleared global stats");
+}
+
+void TimeStats::clearLayersLocked() {
+ ATRACE_CALL();
+
+ mTimeStatsTracker.clear();
+ mTimeStats.stats.clear();
+ ALOGD("Cleared layer stats");
}
bool TimeStats::isEnabled() {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 65e5cf4..cf1c3c6 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -17,6 +17,9 @@
#pragma once
#include <hardware/hwcomposer_defs.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
#include <ui/FenceTime.h>
@@ -37,6 +40,10 @@
public:
virtual ~TimeStats() = default;
+ // Called once boot has been finished to perform additional capabilities,
+ // e.g. registration to statsd.
+ virtual void onBootFinished() = 0;
+
virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
virtual bool isEnabled() = 0;
virtual std::string miniDump() = 0;
@@ -131,6 +138,50 @@
public:
TimeStats();
+ // Delegate to the statsd service and associated APIs.
+ // Production code may use this class directly, whereas unit test may define
+ // a subclass for ease of testing.
+ class StatsEventDelegate {
+ public:
+ virtual ~StatsEventDelegate() = default;
+ virtual struct stats_event* addStatsEventToPullData(pulled_stats_event_list* data) {
+ return add_stats_event_to_pull_data(data);
+ }
+ virtual void registerStatsPullAtomCallback(int32_t atom_tag,
+ stats_pull_atom_callback_t callback,
+ pull_atom_metadata* metadata, void* cookie) {
+ return register_stats_pull_atom_callback(atom_tag, callback, metadata, cookie);
+ }
+
+ virtual void unregisterStatsPullAtomCallback(int32_t atom_tag) {
+ return unregister_stats_pull_atom_callback(atom_tag);
+ }
+
+ virtual void statsEventSetAtomId(struct stats_event* event, uint32_t atom_id) {
+ return stats_event_set_atom_id(event, atom_id);
+ }
+
+ virtual void statsEventWriteInt64(struct stats_event* event, int64_t field) {
+ return stats_event_write_int64(event, field);
+ }
+
+ virtual void statsEventWriteString8(struct stats_event* event, const char* field) {
+ return stats_event_write_string8(event, field);
+ }
+
+ virtual void statsEventWriteByteArray(struct stats_event* event, const uint8_t* buf,
+ size_t numBytes) {
+ return stats_event_write_byte_array(event, buf, numBytes);
+ }
+
+ virtual void statsEventBuild(struct stats_event* event) { return stats_event_build(event); }
+ };
+ // For testing only for injecting custom dependencies.
+ TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
+ std::optional<size_t> maxPulledLayers,
+ std::optional<size_t> maxPulledHistogramBuckets);
+
+ void onBootFinished() override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
std::string miniDump() override;
@@ -167,6 +218,10 @@
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
+ static status_pull_atom_return_t pullAtomCallback(int32_t atom_tag,
+ pulled_stats_event_list* data, void* cookie);
+ status_pull_atom_return_t populateGlobalAtom(pulled_stats_event_list* data);
+ status_pull_atom_return_t populateLayerAtom(pulled_stats_event_list* data);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId);
void flushPowerTimeLocked();
@@ -174,7 +229,9 @@
void enable();
void disable();
- void clear();
+ void clearAll();
+ void clearGlobalLocked();
+ void clearLayersLocked();
void dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result);
std::atomic<bool> mEnabled = false;
@@ -187,6 +244,9 @@
static const size_t MAX_NUM_LAYER_RECORDS = 200;
static const size_t MAX_NUM_LAYER_STATS = 200;
+ std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>();
+ size_t mMaxPulledLayers = 8;
+ size_t mMaxPulledHistogramBuckets = 6;
};
} // namespace impl
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index ef27847..8fce0c9 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -106,6 +106,7 @@
layer.refreshPending = layerProto.refresh_pending();
layer.isProtected = layerProto.is_protected();
layer.cornerRadius = layerProto.corner_radius();
+ layer.backgroundBlurRadius = layerProto.background_blur_radius();
for (const auto& entry : layerProto.metadata()) {
const std::string& dataStr = entry.second;
std::vector<uint8_t>& outData = layer.metadata.mMap[entry.first];
@@ -290,6 +291,7 @@
StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str());
+ StringAppendF(&result, "backgroundBlurRadius=%1d, ", backgroundBlurRadius);
StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
static_cast<double>(color.r), static_cast<double>(color.g),
static_cast<double>(color.b), static_cast<double>(color.a), flags);
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 774b0e1..52b9165 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -110,6 +110,7 @@
bool refreshPending;
bool isProtected;
float cornerRadius;
+ int backgroundBlurRadius;
LayerMetadata metadata;
LayerProtoParser::FloatRect cornerRadiusCrop;
float shadowRadius;
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 41ecafa..8afe503 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -104,6 +104,8 @@
ColorTransformProto color_transform = 50;
bool is_relative_of = 51;
+ // Layer's background blur radius in pixels.
+ int32 background_blur_radius = 52;
}
message PositionProto {
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 049c872..ed2b220 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -371,3 +371,12 @@
access: Readonly
prop_name: "ro.surface_flinger.support_kernel_idle_timer"
}
+
+# Indicates whether background blurs are supported.
+prop {
+ api_name: "supports_background_blur"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.supports_background_blur"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 2d52507..d24ad18 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -104,6 +104,10 @@
prop_name: "ro.surface_flinger.support_kernel_idle_timer"
}
prop {
+ api_name: "supports_background_blur"
+ prop_name: "ro.surface_flinger.supports_background_blur"
+ }
+ prop {
api_name: "use_color_management"
prop_name: "ro.surface_flinger.use_color_management"
}
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 71f01b0..3bbd12a 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -18,6 +18,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#include <cutils/properties.h>
#include <gui/BufferItemConsumer.h>
#include "TransactionTestHarnesses.h"
@@ -243,6 +244,41 @@
shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK);
}
}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadius) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.surface_flinger.supports_background_blur", value, "0");
+ if (!atoi(value)) {
+ // This device doesn't support blurs, no-op.
+ return;
+ }
+
+ auto size = 256;
+ auto center = size / 2;
+ auto blurRadius = 50;
+
+ sp<SurfaceControl> backgroundLayer;
+ ASSERT_NO_FATAL_FAILURE(backgroundLayer = createLayer("background", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(backgroundLayer, Color::GREEN, size, size));
+
+ sp<SurfaceControl> leftLayer;
+ ASSERT_NO_FATAL_FAILURE(leftLayer = createLayer("left", size / 2, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(leftLayer, Color::RED, size / 2, size));
+
+ sp<SurfaceControl> blurLayer;
+ ASSERT_NO_FATAL_FAILURE(blurLayer = createLayer("blur", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(blurLayer, Color::TRANSPARENT, size, size));
+
+ Transaction().setBackgroundBlurRadius(blurLayer, blurRadius).apply();
+
+ auto shot = getScreenCapture();
+ // Edges are mixed
+ shot->expectColor(Rect(center - 1, center - 5, center, center + 5), Color{150, 150, 0, 255},
+ 50 /* tolerance */);
+ shot->expectColor(Rect(center, center - 5, center + 1, center + 5), Color{150, 150, 0, 255},
+ 50 /* tolerance */);
+}
+
TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
sp<SurfaceControl> bufferLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 2bedd7d..e5a7774 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
{
"presubmit": {
- "filter": "*:-LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/3:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/3"
+ "filter": "*:-LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/3:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/3:MirrorLayerTest.MirrorBufferLayer"
}
}
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 75d0761..4a2ab7c 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -53,6 +53,7 @@
constexpr int32_t RELATIVE_Z = 42;
constexpr float ALPHA_UPDATE = 0.29f;
constexpr float CORNER_RADIUS_UPDATE = 0.2f;
+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;
@@ -183,6 +184,8 @@
bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
+ bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
+ bool foundBackgroundBlurRadius);
bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
@@ -220,6 +223,7 @@
void layerUpdate(Transaction&);
void cropUpdate(Transaction&);
void cornerRadiusUpdate(Transaction&);
+ void backgroundBlurRadiusUpdate(Transaction&);
void matrixUpdate(Transaction&);
void overrideScalingModeUpdate(Transaction&);
void transparentRegionHintUpdate(Transaction&);
@@ -355,6 +359,10 @@
t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
}
+void SurfaceInterceptorTest::backgroundBlurRadiusUpdate(Transaction& t) {
+ t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE);
+}
+
void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
}
@@ -432,6 +440,7 @@
runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
+ runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate);
runInTransaction(&SurfaceInterceptorTest::layerUpdate);
runInTransaction(&SurfaceInterceptorTest::cropUpdate);
runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
@@ -509,6 +518,18 @@
return foundCornerRadius;
}
+bool SurfaceInterceptorTest::backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
+ bool foundBackgroundBlur) {
+ bool hasBackgroundBlur(change.background_blur_radius().background_blur_radius() ==
+ BACKGROUND_BLUR_RADIUS_UPDATE);
+ if (hasBackgroundBlur && !foundBackgroundBlur) {
+ foundBackgroundBlur = true;
+ } else if (hasBackgroundBlur && foundBackgroundBlur) {
+ []() { FAIL(); }();
+ }
+ return foundBackgroundBlur;
+}
+
bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
bool hasLayer(change.layer().layer() == LAYER_UPDATE);
if (hasLayer && !foundLayer) {
@@ -705,6 +726,9 @@
case SurfaceChange::SurfaceChangeCase::kCornerRadius:
foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
break;
+ case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius:
+ foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate);
+ break;
case SurfaceChange::SurfaceChangeCase::kMatrix:
foundUpdate = matrixUpdateFound(change, foundUpdate);
break;
@@ -887,6 +911,11 @@
SurfaceChange::SurfaceChangeCase::kCornerRadius);
}
+TEST_F(SurfaceInterceptorTest, InterceptBackgroundBlurRadiusUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate,
+ SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius);
+}
+
TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index becf484..1eaf2dd 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -83,6 +83,8 @@
"perfetto_trace_protos",
],
shared_libs: [
+ "libprotoutil",
+ "libstatssocket",
"libsurfaceflinger",
"libtimestats",
"libtimestats_proto",
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 33563ea..9680a17 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -25,6 +25,7 @@
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/mock/DisplaySurface.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -1278,6 +1279,243 @@
}
/* ------------------------------------------------------------------------
+ * DisplayDevice::setProjection
+ */
+
+class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
+public:
+ static constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
+ static constexpr bool DEFAULT_DISPLAY_IS_VIRTUAL = false;
+ static constexpr bool DEFAULT_DISPLAY_IS_PRIMARY = true;
+ static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080; // arbitrary
+ static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
+
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_270 =
+ HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
+
+ DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize,
+ ui::Rotation physicalOrientation)
+ : mFlingerDisplaySize(flingerDisplaySize),
+ mHardwareDisplaySize(hardwareDisplaySize),
+ mPhysicalOrientation(physicalOrientation),
+ mDisplayDevice(createDisplayDevice()) {}
+
+ sp<DisplayDevice> createDisplayDevice() {
+ // The DisplayDevice is required to have a framebuffer (behind the
+ // ANativeWindow interface) which uses the actual hardware display
+ // size.
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(mHardwareDisplaySize.width), Return(0)));
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(mHardwareDisplaySize.height), Return(0)));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT));
+
+ return FakeDisplayDeviceInjector(mFlinger, DEFAULT_DISPLAY_ID, DEFAULT_DISPLAY_IS_VIRTUAL,
+ DEFAULT_DISPLAY_IS_PRIMARY)
+ .setNativeWindow(mNativeWindow)
+ .setPhysicalOrientation(mPhysicalOrientation)
+ .inject();
+ }
+
+ ui::Size SwapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
+
+ void setProjectionForRotation0() {
+ // A logical rotation of 0 uses the SurfaceFlinger display size
+ mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize),
+ Rect(mFlingerDisplaySize));
+ }
+
+ void setProjectionForRotation90() {
+ // A logical rotation of 90 uses the SurfaceFlinger display size with
+ // the width/height swapped.
+ mDisplayDevice->setProjection(ui::ROTATION_90, Rect(SwapWH(mFlingerDisplaySize)),
+ Rect(SwapWH(mFlingerDisplaySize)));
+ }
+
+ void setProjectionForRotation180() {
+ // A logical rotation of 180 uses the SurfaceFlinger display size
+ mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize),
+ Rect(mFlingerDisplaySize));
+ }
+
+ void setProjectionForRotation270() {
+ // A logical rotation of 270 uses the SurfaceFlinger display size with
+ // the width/height swapped.
+ mDisplayDevice->setProjection(ui::ROTATION_270, Rect(SwapWH(mFlingerDisplaySize)),
+ Rect(SwapWH(mFlingerDisplaySize)));
+ }
+
+ void expectStateForHardwareTransform0() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform90() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ // For 90, the frame and viewport have the hardware display size width and height swapped
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform180() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform270() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.scissor);
+ // For 270, the frame and viewport have the hardware display size width and height swapped
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
+ EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ const ui::Size mFlingerDisplaySize;
+ const ui::Size mHardwareDisplaySize;
+ const ui::Rotation mPhysicalOrientation;
+ const sp<DisplayDevice> mDisplayDevice;
+};
+
+struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed0()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_0) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform270();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed90()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_90) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform0();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed180()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_180) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform90();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed270()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_270) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform180();
+}
+
+/* ------------------------------------------------------------------------
* SurfaceFlinger::getDisplayNativePrimaries
*/
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 9728c80..8ddb872 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -605,6 +605,11 @@
return *this;
}
+ auto& setPhysicalOrientation(ui::Rotation orientation) {
+ mCreationArgs.physicalOrientation = orientation;
+ return *this;
+ }
+
sp<DisplayDevice> inject() {
DisplayDeviceState state;
state.displayId = mCreationArgs.isVirtual ? std::nullopt : mCreationArgs.displayId;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index bcf3ba8..5044626 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -22,6 +22,7 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include <TimeStats/TimeStats.h>
+#include <android/util/ProtoOutputStream.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -40,8 +41,12 @@
namespace android {
namespace {
+using testing::_;
+using testing::AnyNumber;
using testing::Contains;
+using testing::InSequence;
using testing::SizeIs;
+using testing::StrEq;
using testing::UnorderedElementsAre;
// clang-format off
@@ -136,7 +141,42 @@
}
std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
- std::unique_ptr<TimeStats> mTimeStats = std::make_unique<impl::TimeStats>();
+
+ class FakeStatsEventDelegate : public impl::TimeStats::StatsEventDelegate {
+ public:
+ FakeStatsEventDelegate() = default;
+ ~FakeStatsEventDelegate() override = default;
+
+ struct stats_event* addStatsEventToPullData(pulled_stats_event_list*) override {
+ return mEvent;
+ }
+ void registerStatsPullAtomCallback(int32_t atom_tag, stats_pull_atom_callback_t callback,
+ pull_atom_metadata*, void* cookie) override {
+ mAtomTags.push_back(atom_tag);
+ mCallback = callback;
+ mCookie = cookie;
+ }
+
+ status_pull_atom_return_t makePullAtomCallback(int32_t atom_tag, void* cookie) {
+ return (*mCallback)(atom_tag, nullptr, cookie);
+ }
+
+ MOCK_METHOD1(unregisterStatsPullAtomCallback, void(int32_t));
+ MOCK_METHOD2(statsEventSetAtomId, void(struct stats_event*, uint32_t));
+ MOCK_METHOD2(statsEventWriteInt64, void(struct stats_event*, int64_t));
+ MOCK_METHOD2(statsEventWriteString8, void(struct stats_event*, const char*));
+ MOCK_METHOD3(statsEventWriteByteArray, void(struct stats_event*, const uint8_t*, size_t));
+ MOCK_METHOD1(statsEventBuild, void(struct stats_event*));
+
+ struct stats_event* mEvent = stats_event_obtain();
+ std::vector<int32_t> mAtomTags;
+ stats_pull_atom_callback_t mCallback = nullptr;
+ void* mCookie = nullptr;
+ };
+ FakeStatsEventDelegate* mDelegate = new FakeStatsEventDelegate;
+ std::unique_ptr<TimeStats> mTimeStats =
+ std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
+ std::nullopt, std::nullopt);
};
std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
@@ -213,14 +253,26 @@
return distr(mRandomEngine);
}
-TEST_F(TimeStatsTest, enabledByDefault) {
+TEST_F(TimeStatsTest, disabledByDefault) {
+ ASSERT_FALSE(mTimeStats->isEnabled());
+}
+
+TEST_F(TimeStatsTest, enabledAfterBoot) {
+ mTimeStats->onBootFinished();
ASSERT_TRUE(mTimeStats->isEnabled());
}
TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
ASSERT_TRUE(mTimeStats->isEnabled());
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_CALL(*mDelegate,
+ unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+ EXPECT_CALL(*mDelegate,
+ unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
ASSERT_FALSE(mTimeStats->isEnabled());
}
@@ -631,6 +683,319 @@
ASSERT_EQ(0, globalProto.stats_size());
}
+TEST_F(TimeStatsTest, globalStatsCallback) {
+ constexpr size_t TOTAL_FRAMES = 5;
+ constexpr size_t MISSED_FRAMES = 4;
+ constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
+
+ mTimeStats->onBootFinished();
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ for (size_t i = 0; i < TOTAL_FRAMES; i++) {
+ mTimeStats->incrementTotalFrames();
+ }
+ for (size_t i = 0; i < MISSED_FRAMES; i++) {
+ mTimeStats->incrementMissedFrames();
+ }
+ for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) {
+ mTimeStats->incrementClientCompositionFrames();
+ }
+
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*mDelegate,
+ statsEventSetAtomId(mDelegate->mEvent,
+ android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, TOTAL_FRAMES));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, MISSED_FRAMES));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, CLIENT_COMPOSITION_FRAMES));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, _));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 2));
+ EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
+ }
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ mDelegate->mCookie));
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_EQ(0, globalProto.total_frames());
+ EXPECT_EQ(0, globalProto.missed_frames());
+ EXPECT_EQ(0, globalProto.client_composition_frames());
+ EXPECT_EQ(0, globalProto.present_to_present_size());
+}
+
+namespace {
+std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times,
+ const std::vector<int32_t>& frameCounts) {
+ util::ProtoOutputStream proto;
+ for (int i = 0; i < times.size(); i++) {
+ ALOGE("Writing time: %d", times[i]);
+ proto.write(util::FIELD_TYPE_INT32 | util::FIELD_COUNT_REPEATED | 1 /* field id */,
+ (int32_t)times[i]);
+ ALOGE("Writing count: %d", frameCounts[i]);
+ proto.write(util::FIELD_TYPE_INT64 | util::FIELD_COUNT_REPEATED | 2 /* field id */,
+ (int64_t)frameCounts[i]);
+ }
+ std::string byteString;
+ proto.serializeToString(&byteString);
+ return byteString;
+}
+
+std::string dumpByteStringHex(const std::string& str) {
+ std::stringstream ss;
+ ss << std::hex;
+ for (const char& c : str) {
+ ss << (int)c << " ";
+ }
+
+ return ss.str();
+}
+
+} // namespace
+
+MATCHER_P2(BytesEq, bytes, size, "") {
+ std::string expected;
+ expected.append((const char*)bytes, size);
+ std::string actual;
+ actual.append((const char*)arg, size);
+
+ *result_listener << "Bytes are not equal! \n";
+ *result_listener << "size: " << size << "\n";
+ *result_listener << "expected: " << dumpByteStringHex(expected).c_str() << "\n";
+ *result_listener << "actual: " << dumpByteStringHex(actual).c_str() << "\n";
+
+ return expected == actual;
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsAllHistogramsAndClears) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ mTimeStats->onBootFinished();
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {1});
+ std::string expectedPostToPresent = buildExpectedHistogramBytestring({4}, {1});
+ std::string expectedAcquireToPresent = buildExpectedHistogramBytestring({3}, {1});
+ std::string expectedLatchToPresent = buildExpectedHistogramBytestring({2}, {1});
+ std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1});
+ std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1});
+ {
+ InSequence seq;
+ EXPECT_CALL(*mDelegate,
+ statsEventSetAtomId(mDelegate->mEvent,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteString8(mDelegate->mEvent,
+ StrEq(genLayerName(LAYER_ID_0).c_str())));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 0));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)
+ expectedPresentToPresent.c_str(),
+ expectedPresentToPresent.size()),
+ expectedPresentToPresent.size()));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)expectedPostToPresent.c_str(),
+ expectedPostToPresent.size()),
+ expectedPostToPresent.size()));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)
+ expectedAcquireToPresent.c_str(),
+ expectedAcquireToPresent.size()),
+ expectedAcquireToPresent.size()));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)expectedLatchToPresent.c_str(),
+ expectedLatchToPresent.size()),
+ expectedLatchToPresent.size()));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)
+ expectedDesiredToPresent.c_str(),
+ expectedDesiredToPresent.size()),
+ expectedDesiredToPresent.size()));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)expectedPostToAcquire.c_str(),
+ expectedPostToAcquire.size()),
+ expectedPostToAcquire.size()));
+ EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
+ }
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ mDelegate->mCookie));
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ mTimeStats->onBootFinished();
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ EXPECT_CALL(*mDelegate,
+ statsEventSetAtomId(mDelegate->mEvent,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO))
+ .Times(2);
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_0).c_str())));
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleBuckets) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ mTimeStats->onBootFinished();
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+
+ // Now make sure that TimeStats flushes global stats to register the
+ // callback.
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1, 2}, {2, 1});
+ {
+ InSequence seq;
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)
+ expectedPresentToPresent.c_str(),
+ expectedPresentToPresent.size()),
+ expectedPresentToPresent.size()));
+ EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
+ .Times(AnyNumber());
+ }
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_limitsHistogramBuckets) {
+ mDelegate = new FakeStatsEventDelegate;
+ mTimeStats =
+ std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
+ std::nullopt, 1);
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ mTimeStats->onBootFinished();
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {2});
+ {
+ InSequence seq;
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteByteArray(mDelegate->mEvent,
+ BytesEq((const uint8_t*)
+ expectedPresentToPresent.c_str(),
+ expectedPresentToPresent.size()),
+ expectedPresentToPresent.size()));
+ EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
+ .Times(AnyNumber());
+ }
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_limitsLayers) {
+ mDelegate = new FakeStatsEventDelegate;
+ mTimeStats =
+ std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate), 1,
+ std::nullopt);
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ mTimeStats->onBootFinished();
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
+
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ EXPECT_CALL(*mDelegate,
+ statsEventSetAtomId(mDelegate->mEvent,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO))
+ .Times(1);
+ EXPECT_CALL(*mDelegate,
+ statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ mDelegate->mCookie));
+}
+
TEST_F(TimeStatsTest, canSurviveMonkey) {
if (g_noSlowTests) {
GTEST_SKIP();
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index b51a025..acf852d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -50,6 +50,7 @@
nsecs_t currentPeriod() const final { return mPeriod; }
void setPeriod(nsecs_t) final {}
+ void resetModel() final {}
private:
nsecs_t const mPeriod;
@@ -83,6 +84,7 @@
}
void setPeriod(nsecs_t) final {}
+ void resetModel() final {}
private:
std::mutex mutable mMutex;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index f5649ee..70c9225 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -45,6 +45,7 @@
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
+ MOCK_METHOD0(resetModel, void());
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
if (timePoint % mPeriod == 0) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 00d3cc6..6ec3844 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -355,6 +355,21 @@
EXPECT_THAT(prediction, Ge(timePoint));
}
+TEST_F(VSyncPredictorTest, resetsWhenInstructed) {
+ auto const idealPeriod = 10000;
+ auto const realPeriod = 10500;
+ tracker.setPeriod(idealPeriod);
+ for (auto i = 0; i < kMinimumSamplesForPrediction; i++) {
+ tracker.addVsyncTimestamp(i * realPeriod);
+ }
+
+ EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
+ IsCloseTo(realPeriod, mMaxRoundingError));
+ tracker.resetModel();
+ EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
+ IsCloseTo(idealPeriod, mMaxRoundingError));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 188adea..ce1fafe 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -39,6 +39,7 @@
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
+ MOCK_METHOD0(resetModel, void());
};
class VSyncTrackerWrapper : public VSyncTracker {
@@ -50,7 +51,8 @@
return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
}
nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
- void setPeriod(nsecs_t period) { mTracker->setPeriod(period); }
+ void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
+ void resetModel() final { mTracker->resetModel(); }
private:
std::shared_ptr<VSyncTracker> const mTracker;
@@ -559,6 +561,11 @@
mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime);
}
+TEST_F(VSyncReactorTest, beginResyncResetsModel) {
+ EXPECT_CALL(*mMockTracker, resetModel());
+ mReactor.beginResync();
+}
+
using VSyncReactorDeathTest = VSyncReactorTest;
TEST_F(VSyncReactorDeathTest, invalidRemoval) {
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index ec74a42..9ada5ef 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -28,6 +28,7 @@
TimeStats();
~TimeStats() override;
+ MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
MOCK_METHOD0(isEnabled, bool());
MOCK_METHOD0(miniDump, std::string());