Merge "SF: Add DisplayDevice setProjection test"
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.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/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/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/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 9154191..e8c47a5 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -42,7 +42,7 @@
kMinimumSamplesForPrediction(minimumSamplesForPrediction),
kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
mIdealPeriod(idealPeriod) {
- mRateMap[mIdealPeriod] = {idealPeriod, 0};
+ resetModel();
}
inline size_t VSyncPredictor::next(int i) const {
@@ -203,6 +203,10 @@
mRateMap[mIdealPeriod] = {period, 0};
}
+ clearTimestamps();
+}
+
+void VSyncPredictor::clearTimestamps() {
if (!timestamps.empty()) {
mKnownTimestamp = *std::max_element(timestamps.begin(), timestamps.end());
timestamps.clear();
@@ -227,6 +231,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 4210b3c..41e5469 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -40,6 +40,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.
@@ -48,7 +49,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.
@@ -61,6 +62,7 @@
private:
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
+ void clearTimestamps() REQUIRES(mMutex);
size_t const kHistorySize;
size_t const kMinimumSamplesForPrediction;
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 06a9b1f..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;
@@ -581,6 +589,7 @@
.setUseColorManagerment(useColorManagement)
.setEnableProtectedContext(enable_protected_contents(false))
.setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(mEnableBlurs)
.setContextPriority(useContextPriority
? renderengine::RenderEngine::ContextPriority::HIGH
: renderengine::RenderEngine::ContextPriority::MEDIUM)
@@ -876,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();
@@ -953,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());
}
@@ -3380,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 7ff2594..d27fbb4 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -8,6 +8,7 @@
"libcutils",
"liblog",
"libprotobuf-cpp-lite",
+ "libprotoutil",
"libstatslog",
"libstatspull",
"libstatssocket",
@@ -17,6 +18,7 @@
],
export_include_dirs: ["."],
export_shared_lib_headers: [
+ "libprotoutil",
"libstatslog",
"libstatspull",
"libstatssocket",
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 44a59fd..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,42 +37,134 @@
namespace impl {
-status_pull_atom_return_t TimeStats::pullGlobalAtomCallback(int32_t atom_tag,
- pulled_stats_event_list* data,
- void* cookie) {
+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 STATS_PULL_SKIP;
+ 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);
}
- std::lock_guard<std::mutex> lock(timeStats->mMutex);
+ return STATS_PULL_SKIP;
+}
- const auto& stats = timeStats->mTimeStats;
- if (stats.statsStart == 0) {
+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;
}
- timeStats->flushPowerTimeLocked();
+ flushPowerTimeLocked();
- struct stats_event* event = timeStats->mStatsDelegate->addStatsEventToPullData(data);
- timeStats->mStatsDelegate->statsEventSetAtomId(event,
- android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.totalFrames);
- timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.missedFrames);
- timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.clientCompositionFrames);
- timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.displayOnTime);
- timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.presentToPresent.totalTime());
- timeStats->mStatsDelegate->statsEventBuild(event);
- timeStats->clearGlobalLocked();
+ 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;
}
-TimeStats::TimeStats() : TimeStats(nullptr) {}
+namespace {
+// Histograms align with the order of fields in SurfaceflingerStatsLayerInfo.
+const std::array<std::string, 6> kHistogramNames = {
+ "present2present", "post2present", "acquire2present",
+ "latch2present", "desired2present", "post2acquire",
+};
-TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate) {
+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() {
@@ -634,7 +727,9 @@
mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
mPowerTime.prevTime = systemTime();
mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- TimeStats::pullGlobalAtomCallback, nullptr, this);
+ TimeStats::pullAtomCallback, nullptr, this);
+ mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ TimeStats::pullAtomCallback, nullptr, this);
ALOGD("Enabled");
}
@@ -649,6 +744,7 @@
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");
}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 5cd421c..cf1c3c6 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -165,10 +165,21 @@
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);
+ 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;
@@ -207,9 +218,10 @@
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
- static status_pull_atom_return_t pullGlobalAtomCallback(int32_t atom_tag,
- pulled_stats_event_list* data,
- void* cookie);
+ 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();
@@ -233,6 +245,8 @@
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/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 e500672..1eaf2dd 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -83,6 +83,7 @@
"perfetto_trace_protos",
],
shared_libs: [
+ "libprotoutil",
"libstatssocket",
"libsurfaceflinger",
"libtimestats",
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index c667080..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>
@@ -41,9 +42,11 @@
namespace {
using testing::_;
+using testing::AnyNumber;
using testing::Contains;
using testing::InSequence;
using testing::SizeIs;
+using testing::StrEq;
using testing::UnorderedElementsAre;
// clang-format off
@@ -149,7 +152,7 @@
}
void registerStatsPullAtomCallback(int32_t atom_tag, stats_pull_atom_callback_t callback,
pull_atom_metadata*, void* cookie) override {
- mAtomTag = atom_tag;
+ mAtomTags.push_back(atom_tag);
mCallback = callback;
mCookie = cookie;
}
@@ -161,17 +164,19 @@
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();
- int32_t mAtomTag = 0;
+ 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::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
+ std::nullopt, std::nullopt);
};
std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
@@ -260,10 +265,14 @@
TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
ASSERT_TRUE(mTimeStats->isEnabled());
- EXPECT_EQ(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, mDelegate->mAtomTag);
+ 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());
}
@@ -696,7 +705,9 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
- EXPECT_EQ(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, mDelegate->mAtomTag);
+ 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);
@@ -713,7 +724,8 @@
EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
}
EXPECT_EQ(STATS_PULL_SUCCESS,
- mDelegate->makePullAtomCallback(mDelegate->mAtomTag, mDelegate->mCookie));
+ mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ mDelegate->mCookie));
SFTimeStatsGlobalProto globalProto;
ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
@@ -724,6 +736,266 @@
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);