Merge "SF: Start restructuring display creation" into rvc-dev
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index cf75bba..544e26c 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -571,81 +571,6 @@
return true;
}
-// Poke all the binder-enabled processes in the system to get them to re-read
-// their system properties.
-static bool pokeBinderServices()
-{
- sp<IServiceManager> sm = defaultServiceManager();
- Vector<String16> services = sm->listServices();
- for (size_t i = 0; i < services.size(); i++) {
- sp<IBinder> obj = sm->checkService(services[i]);
- if (obj != nullptr) {
- Parcel data;
- if (obj->transact(IBinder::SYSPROPS_TRANSACTION, data,
- nullptr, 0) != OK) {
- if (false) {
- // XXX: For some reason this fails on tablets trying to
- // poke the "phone" service. It's not clear whether some
- // are expected to fail.
- String8 svc(services[i]);
- fprintf(stderr, "error poking binder service %s\n",
- svc.string());
- return false;
- }
- }
- }
- }
- return true;
-}
-
-// Poke all the HAL processes in the system to get them to re-read
-// their system properties.
-static void pokeHalServices()
-{
- using ::android::hidl::base::V1_0::IBase;
- using ::android::hidl::manager::V1_0::IServiceManager;
- using ::android::hardware::hidl_string;
- using ::android::hardware::Return;
-
- sp<IServiceManager> sm = ::android::hardware::defaultServiceManager();
-
- if (sm == nullptr) {
- fprintf(stderr, "failed to get IServiceManager to poke hal services\n");
- return;
- }
-
- auto listRet = sm->list([&](const auto &interfaces) {
- for (size_t i = 0; i < interfaces.size(); i++) {
- string fqInstanceName = interfaces[i];
- string::size_type n = fqInstanceName.find('/');
- if (n == std::string::npos || interfaces[i].size() == n+1)
- continue;
- hidl_string fqInterfaceName = fqInstanceName.substr(0, n);
- hidl_string instanceName = fqInstanceName.substr(n+1, std::string::npos);
- Return<sp<IBase>> interfaceRet = sm->get(fqInterfaceName, instanceName);
- if (!interfaceRet.isOk()) {
- // ignore
- continue;
- }
-
- sp<IBase> interface = interfaceRet;
- if (interface == nullptr) {
- // ignore
- continue;
- }
-
- auto notifyRet = interface->notifySyspropsChanged();
- if (!notifyRet.isOk()) {
- // ignore
- }
- }
- });
- if (!listRet.isOk()) {
- // TODO(b/34242478) fix this when we determine the correct ACL
- //fprintf(stderr, "failed to list services: %s\n", listRet.description().c_str());
- }
-}
-
// Set the trace tags that userland tracing uses, and poke the running
// processes to pick up the new value.
static bool setTagsProperty(uint64_t tags)
@@ -876,10 +801,6 @@
}
ok &= setAppCmdlineProperty(&packageList[0]);
ok &= setTagsProperty(tags);
-#if !ATRACE_SHMEM
- ok &= pokeBinderServices();
- pokeHalServices();
-#endif
if (g_tracePdx) {
ok &= ServiceUtility::PokeServices();
}
@@ -891,8 +812,6 @@
{
setTagsProperty(0);
clearAppProperties();
- pokeBinderServices();
- pokeHalServices();
if (g_tracePdx) {
ServiceUtility::PokeServices();
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 3d77059..1075f16 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -59,8 +59,6 @@
"gl/Program.cpp",
"gl/ProgramCache.cpp",
"gl/filters/BlurFilter.cpp",
- "gl/filters/KawaseBlurFilter.cpp",
- "gl/filters/GaussianBlurFilter.cpp",
"gl/filters/GenericProgram.cpp",
],
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index e11b59f..daf7d72 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -50,8 +50,6 @@
#include "Program.h"
#include "ProgramCache.h"
#include "filters/BlurFilter.h"
-#include "filters/GaussianBlurFilter.h"
-#include "filters/KawaseBlurFilter.h"
extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
@@ -430,13 +428,7 @@
}
if (args.supportsBackgroundBlur) {
- char isGaussian[PROPERTY_VALUE_MAX];
- property_get("debug.sf.gaussianBlur", isGaussian, "0");
- if (atoi(isGaussian)) {
- mBlurFilter = new GaussianBlurFilter(*this);
- } else {
- mBlurFilter = new KawaseBlurFilter(*this);
- }
+ mBlurFilter = new BlurFilter(*this);
checkErrors("BlurFilter creation");
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index ebf78fe..4cd0b3d 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -261,8 +261,6 @@
friend class ImageManager;
friend class GLFramebuffer;
friend class BlurFilter;
- friend class GaussianBlurFilter;
- friend class KawaseBlurFilter;
friend class GenericProgram;
std::unique_ptr<FlushTracer> mFlushTracer;
std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index eb66c8f..e704907 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -31,13 +31,24 @@
namespace gl {
BlurFilter::BlurFilter(GLESRenderEngine& engine)
- : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mMixProgram(engine) {
+ : mEngine(engine),
+ mCompositionFbo(engine),
+ mPingFbo(engine),
+ mPongFbo(engine),
+ mMixProgram(engine),
+ mBlurProgram(engine) {
mMixProgram.compile(getVertexShader(), getMixFragShader());
mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
mMUvLoc = mMixProgram.getAttributeLocation("aUV");
mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
mMMixLoc = mMixProgram.getUniformLocation("uMix");
+
+ mBlurProgram.compile(getVertexShader(), getFragmentShader());
+ mBPosLoc = mBlurProgram.getAttributeLocation("aPosition");
+ mBUvLoc = mBlurProgram.getAttributeLocation("aUV");
+ mBTextureLoc = mBlurProgram.getUniformLocation("uTexture");
+ mBOffsetLoc = mBlurProgram.getUniformLocation("uOffset");
}
status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
@@ -51,14 +62,14 @@
const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
- mBlurredFbo.allocateBuffers(fboWidth, fboHeight);
- allocateTextures();
+ mPingFbo.allocateBuffers(fboWidth, fboHeight);
+ mPongFbo.allocateBuffers(fboWidth, fboHeight);
mTexturesAllocated = true;
}
- if (mBlurredFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
ALOGE("Invalid blur buffer");
- return mBlurredFbo.getStatus();
+ return mPingFbo.getStatus();
}
if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
ALOGE("Invalid composition buffer");
@@ -96,6 +107,61 @@
mEngine.checkErrors("Drawing blur mesh");
}
+status_t BlurFilter::prepare() {
+ ATRACE_NAME("BlurFilter::prepare");
+
+ if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid FBO");
+ return mPongFbo.getStatus();
+ }
+ if (!mBlurProgram.isValid()) {
+ ALOGE("Invalid shader");
+ return GL_INVALID_OPERATION;
+ }
+
+ blit(mCompositionFbo, mPingFbo);
+
+ // Kawase is an approximation of Gaussian, but it behaves differently from it.
+ // A radius transformation is required for approximating them, and also to introduce
+ // non-integer steps, necessary to smoothly interpolate large radii.
+ auto radius = mRadius / 6.0f;
+
+ // Calculate how many passes we'll do, based on the radius.
+ // Too many passes will make the operation expensive.
+ auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
+
+ // We'll ping pong between our textures, to accumulate the result of various offsets.
+ mBlurProgram.useProgram();
+ GLFramebuffer* read = &mPingFbo;
+ GLFramebuffer* draw = &mPongFbo;
+ float stepX = radius / (float)mCompositionFbo.getBufferWidth() / (float)passes;
+ float stepY = radius / (float)mCompositionFbo.getBufferHeight() / (float)passes;
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(mBTextureLoc, 0);
+ for (auto i = 0; i < passes; i++) {
+ ATRACE_NAME("BlurFilter::renderPass");
+ draw->bind();
+
+ glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
+ glBindTexture(GL_TEXTURE_2D, read->getTextureName());
+ glUniform2f(mBOffsetLoc, stepX * i, stepY * i);
+ mEngine.checkErrors("Setting uniforms");
+
+ drawMesh(mBUvLoc, mBPosLoc);
+
+ // Swap buffers for next iteration
+ auto tmp = draw;
+ draw = read;
+ read = tmp;
+ }
+ mLastDrawTarget = read;
+
+ // Cleanup
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ return NO_ERROR;
+}
+
status_t BlurFilter::render(bool multiPass) {
ATRACE_NAME("BlurFilter::render");
@@ -107,9 +173,10 @@
// be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer,
// as large as the screen size.
if (mix >= 1 || multiPass) {
- mBlurredFbo.bindAsReadBuffer();
- glBlitFramebuffer(0, 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(), 0, 0,
- mDisplayWidth, mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ mLastDrawTarget->bindAsReadBuffer();
+ glBlitFramebuffer(0, 0, mLastDrawTarget->getBufferWidth(),
+ mLastDrawTarget->getBufferHeight(), 0, 0, mDisplayWidth, mDisplayHeight,
+ GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
return NO_ERROR;
}
@@ -117,7 +184,7 @@
mMixProgram.useProgram();
glUniform1f(mMMixLoc, mix);
glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
+ glBindTexture(GL_TEXTURE_2D, mLastDrawTarget->getTextureName());
glUniform1i(mMTextureLoc, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
@@ -145,6 +212,28 @@
)SHADER";
}
+string BlurFilter::getFragmentShader() const {
+ return R"SHADER(#version 310 es
+ precision mediump float;
+
+ uniform sampler2D uTexture;
+ uniform vec2 uOffset;
+
+ highp in vec2 vUV;
+ out vec4 fragColor;
+
+ void main() {
+ fragColor = texture(uTexture, vUV, 0.0);
+ fragColor += texture(uTexture, vUV + vec2( uOffset.x, uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2(-uOffset.x, uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);
+
+ fragColor = vec4(fragColor.rgb * 0.2, 1.0);
+ }
+ )SHADER";
+}
+
string BlurFilter::getMixFragShader() const {
string shader = R"SHADER(#version 310 es
precision mediump float;
@@ -165,6 +254,15 @@
return shader;
}
+void BlurFilter::blit(GLFramebuffer& read, GLFramebuffer& draw) const {
+ read.bindAsReadBuffer();
+ draw.bindAsDrawBuffer();
+ glBlitFramebuffer(0, 0, read.getBufferWidth(), read.getBufferHeight(), 0, 0,
+ draw.getBufferWidth(), draw.getBufferHeight(), GL_COLOR_BUFFER_BIT,
+ GL_LINEAR);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
} // namespace gl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index 52dc8aa..eb6120b 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -27,10 +27,17 @@
namespace renderengine {
namespace gl {
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
class BlurFilter {
public:
// Downsample FBO to improve performance
static constexpr float kFboScale = 0.25f;
+ // Maximum number of render passes
+ static constexpr uint32_t kMaxPasses = 6;
// To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
// image, up to this radius.
static constexpr float kMaxCrossFadeRadius = 30.0f;
@@ -40,28 +47,29 @@
// Set up render targets, redirecting output to offscreen texture.
status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
- // Allocate any textures needed for the filter.
- virtual void allocateTextures() = 0;
// Execute blur passes, rendering to offscreen texture.
- virtual status_t prepare() = 0;
+ status_t prepare();
// Render blur to the bound framebuffer (screen).
status_t render(bool multiPass);
-protected:
+private:
uint32_t mRadius;
void drawMesh(GLuint uv, GLuint position);
+ void blit(GLFramebuffer& read, GLFramebuffer& draw) const;
string getVertexShader() const;
+ string getFragmentShader() const;
+ string getMixFragShader() const;
GLESRenderEngine& mEngine;
// Frame buffer holding the composited background.
GLFramebuffer mCompositionFbo;
- // Frame buffer holding the blur result.
- GLFramebuffer mBlurredFbo;
+ // Frame buffers holding the blur passes.
+ GLFramebuffer mPingFbo;
+ GLFramebuffer mPongFbo;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
-
-private:
- string getMixFragShader() const;
+ // Buffer holding the final blur pass.
+ GLFramebuffer* mLastDrawTarget;
bool mTexturesAllocated = false;
GenericProgram mMixProgram;
@@ -70,6 +78,12 @@
GLuint mMMixLoc;
GLuint mMTextureLoc;
GLuint mMCompositionTextureLoc;
+
+ GenericProgram mBlurProgram;
+ GLuint mBPosLoc;
+ GLuint mBUvLoc;
+ GLuint mBTextureLoc;
+ GLuint mBOffsetLoc;
};
} // namespace gl
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
deleted file mode 100644
index a0d7af8..0000000
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * 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>
-
-#define PI 3.14159265359
-#define THETA 0.352
-#define K 1.0 / (2.0 * THETA * THETA)
-
-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");
- mVGaussianOffsetLoc = mVerticalProgram.getUniformLocation("uGaussianOffsets");
- mVNumSamplesLoc = mVerticalProgram.getUniformLocation("uSamples");
- mVGaussianWeightLoc = mVerticalProgram.getUniformLocation("uGaussianWeights");
-
- mHorizontalProgram.compile(getVertexShader(), getFragmentShader(true));
- mHPosLoc = mHorizontalProgram.getAttributeLocation("aPosition");
- mHUvLoc = mHorizontalProgram.getAttributeLocation("aUV");
- mHTextureLoc = mHorizontalProgram.getUniformLocation("uTexture");
- mHGaussianOffsetLoc = mHorizontalProgram.getUniformLocation("uGaussianOffsets");
- mHNumSamplesLoc = mHorizontalProgram.getUniformLocation("uSamples");
- mHGaussianWeightLoc = mHorizontalProgram.getUniformLocation("uGaussianWeights");
-}
-
-void GaussianBlurFilter::allocateTextures() {
- mVerticalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
-}
-
-static void calculateLinearGaussian(uint32_t samples, double dimension,
- GLfloat* gaussianLinearOffsets, GLfloat* gaussianWeights,
- GLfloat* gaussianLinearWeights) {
- // The central point in the symmetric bell curve is not offset.
- // This decision allows one less sampling in the GPU.
- gaussianLinearWeights[0] = gaussianWeights[0];
- gaussianLinearOffsets[0] = 0.0;
-
- // Calculate the linear weights.
- // This is a vector reduction where an element of the packed reduced array
- // contains the sum of two adjacent members of the original packed array.
- // We start preserving the element 1 of the array and then perform sum for
- // every other (i+=2) element of the gaussianWeights array.
- gaussianLinearWeights[1] = gaussianWeights[1];
- const auto start = 1 + ((samples - 1) & 0x1);
- for (size_t i = start; i < samples; i += 2) {
- gaussianLinearWeights[start + i / 2] = gaussianWeights[i] + gaussianWeights[i + 1];
- }
-
- // Calculate the texture coordinates offsets as an average of the initial offsets,
- // weighted by the Gaussian weights as described in the original article.
- gaussianLinearOffsets[1] = 1.0 / dimension;
- for (size_t i = start; i < samples; i += 2) {
- GLfloat offset_1 = float(i) / dimension;
- GLfloat offset_2 = float(i + 1) / dimension;
- gaussianLinearOffsets[start + i / 2] =
- (offset_1 * gaussianWeights[i] + offset_2 * gaussianWeights[i + 1]) /
- gaussianLinearWeights[start + i / 2];
- }
-}
-status_t GaussianBlurFilter::prepare() {
- 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;
- }
-
- mCompositionFbo.bindAsReadBuffer();
- mBlurredFbo.bindAsDrawBuffer();
- glBlitFramebuffer(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight(), 0,
- 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(),
- GL_COLOR_BUFFER_BIT, GL_LINEAR);
- glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
-
- // First, we'll apply the vertical pass, that receives the flattened background layers.
- mVerticalPassFbo.bind();
- mVerticalProgram.useProgram();
-
- // Precompute gaussian bell curve, and send it to the shader to avoid unnecessary computations.
- double radiusD = fmax(1.0, mRadius * kFboScale);
- auto samples = int(fmin(radiusD, kNumSamples));
- GLfloat gaussianWeights[kNumSamples] = {};
-
- gaussianWeights[0] = 1.0f;
- auto totalWeight = gaussianWeights[0];
-
- // Gaussian weights calculation.
- for (size_t i = 1; i < samples; i++) {
- const double normalized = i / radiusD;
- gaussianWeights[i] = (float)exp(-K * normalized * normalized);
- totalWeight += 2.0 * gaussianWeights[i];
- }
-
- // Gaussian weights normalization to avoid work in the GPU.
- for (size_t i = 0; i < samples; i++) {
- gaussianWeights[i] /= totalWeight;
- }
-
- auto width = mVerticalPassFbo.getBufferWidth();
- auto height = mVerticalPassFbo.getBufferHeight();
- glViewport(0, 0, width, height);
-
- // Allocate space for the corrected Gaussian weights and offsets.
- // We could use less space, but let's keep the code simple.
- GLfloat gaussianLinearWeights[kNumSamples] = {};
- GLfloat gaussianLinearOffsets[kNumSamples] = {};
-
- // Calculate the weights and offsets for the vertical pass.
- // This only need to be called every time mRadius or height changes, so it could be optimized.
- calculateLinearGaussian(samples, double(height), gaussianLinearOffsets, gaussianWeights,
- gaussianLinearWeights);
- // set uniforms
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
- glUniform1i(mVTextureLoc, 0);
- glUniform1i(mVNumSamplesLoc, 1 + (samples + 1) / 2);
- glUniform1fv(mVGaussianWeightLoc, kNumSamples, gaussianLinearWeights);
- glUniform1fv(mVGaussianOffsetLoc, kNumSamples, gaussianLinearOffsets);
- mEngine.checkErrors("Setting vertical pass uniforms");
-
- drawMesh(mVUvLoc, mVPosLoc);
-
- // Blur vertically on a secondary pass
- mBlurredFbo.bind();
- mHorizontalProgram.useProgram();
-
- // Calculate the weights and offsets for the horizontal pass.
- // This only needs to be called every time mRadius or width change, so it could be optimized.
- calculateLinearGaussian(samples, double(width), gaussianLinearOffsets, gaussianWeights,
- gaussianLinearWeights);
- // set uniforms
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mVerticalPassFbo.getTextureName());
- glUniform1i(mHTextureLoc, 0);
- glUniform1i(mHNumSamplesLoc, 1 + (samples + 1) / 2);
- glUniform1fv(mHGaussianWeightLoc, kNumSamples, gaussianLinearWeights);
- glUniform1fv(mHGaussianOffsetLoc, kNumSamples, gaussianLinearOffsets);
- mEngine.checkErrors("Setting horizontal 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 {
- stringstream shader;
- shader << "#version 310 es\n"
- << "#define DIRECTION " << (horizontal ? "1" : "0") << "\n"
- << "#define NUM_SAMPLES " << 1 + (kNumSamples + 1) / 2 <<
- R"SHADER(
- precision mediump float;
-
- uniform sampler2D uTexture;
- uniform float[NUM_SAMPLES] uGaussianWeights;
- uniform float[NUM_SAMPLES] uGaussianOffsets;
- uniform int uSamples;
-
- highp in vec2 vUV;
- out vec4 fragColor;
-
- void main() {
- #if DIRECTION == 1
- const vec2 direction = vec2(1.0, 0.0);
- #else
- const vec2 direction = vec2(0.0, 1.0);
- #endif
-
- // Iteration zero outside loop to avoid sampling the central point twice.
- vec4 blurred = uGaussianWeights[0] * (texture(uTexture, vUV, 0.0));
-
- // Iterate one side of the bell to halve the loop iterations.
- for (int i = 1; i <= uSamples; i++) {
- vec2 offset = uGaussianOffsets[i] * direction;
- blurred += uGaussianWeights[i] * (texture(uTexture, vUV + offset, 0.0));
- blurred += uGaussianWeights[i] * (texture(uTexture, vUV - offset, 0.0));
- }
-
- fragColor = vec4(blurred.rgb, 1.0);
- }
- )SHADER";
- return shader.str();
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.h b/libs/renderengine/gl/filters/GaussianBlurFilter.h
deleted file mode 100644
index 44f5fde..0000000
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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 that implements a Gaussian Filter that uses Linear Sampling
-// to halve the number of samples and reduce runtime by 40% as described in:
-// http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling
-class GaussianBlurFilter : public BlurFilter {
-public:
- static constexpr uint32_t kNumSamples = 22;
-
- explicit GaussianBlurFilter(GLESRenderEngine& engine);
- status_t prepare() 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 mVGaussianOffsetLoc;
- GLuint mVNumSamplesLoc;
- GLuint mVGaussianWeightLoc;
-
- // Horizontal pass and its uniforms
- GenericProgram mHorizontalProgram;
- GLuint mHPosLoc;
- GLuint mHUvLoc;
- GLuint mHTextureLoc;
- GLuint mHGaussianOffsetLoc;
- GLuint mHNumSamplesLoc;
- GLuint mHGaussianWeightLoc;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/gl/filters/KawaseBlurFilter.cpp b/libs/renderengine/gl/filters/KawaseBlurFilter.cpp
deleted file mode 100644
index 7524c6d..0000000
--- a/libs/renderengine/gl/filters/KawaseBlurFilter.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "KawaseBlurFilter.h"
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-#include <GLES3/gl3ext.h>
-#include <ui/GraphicTypes.h>
-
-#include <utils/Trace.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-KawaseBlurFilter::KawaseBlurFilter(GLESRenderEngine& engine)
- : BlurFilter(engine), mFbo(engine), mProgram(engine) {
- mProgram.compile(getVertexShader(), getFragmentShader());
- mPosLoc = mProgram.getAttributeLocation("aPosition");
- mUvLoc = mProgram.getAttributeLocation("aUV");
- mTextureLoc = mProgram.getUniformLocation("uTexture");
- mOffsetLoc = mProgram.getUniformLocation("uOffset");
-}
-
-void KawaseBlurFilter::allocateTextures() {
- mFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
-}
-
-status_t KawaseBlurFilter::prepare() {
- ATRACE_NAME("KawaseBlurFilter::prepare");
-
- if (mFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid FBO");
- return mFbo.getStatus();
- }
- if (!mProgram.isValid()) {
- ALOGE("Invalid shader");
- return GL_INVALID_OPERATION;
- }
-
- blit(mCompositionFbo, mBlurredFbo);
-
- // Kawase is an approximation of Gaussian, but it behaves differently from it.
- // A radius transformation is required for approximating them, and also to introduce
- // non-integer steps, necessary to smoothly interpolate large radii.
- auto radius = mRadius / 6.0f;
-
- // Calculate how many passes we'll do, based on the radius.
- // Too many passes will make the operation expensive.
- auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
-
- // We'll ping pong between our textures, to accumulate the result of various offsets.
- mProgram.useProgram();
- GLFramebuffer* draw = &mFbo;
- GLFramebuffer* read = &mBlurredFbo;
- float stepX = radius / (float)mCompositionFbo.getBufferWidth() / (float)passes;
- float stepY = radius / (float)mCompositionFbo.getBufferHeight() / (float)passes;
- glActiveTexture(GL_TEXTURE0);
- glUniform1i(mTextureLoc, 0);
- for (auto i = 0; i < passes; i++) {
- ATRACE_NAME("KawaseBlurFilter::renderPass");
- draw->bind();
-
- glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
- glBindTexture(GL_TEXTURE_2D, read->getTextureName());
- glUniform2f(mOffsetLoc, stepX * i, stepY * i);
- mEngine.checkErrors("Setting uniforms");
-
- drawMesh(mUvLoc, mPosLoc);
-
- // Swap buffers for next iteration
- auto tmp = draw;
- draw = read;
- read = tmp;
- }
-
- // Copy texture, given that we're expected to end on mBlurredFbo.
- if (draw == &mBlurredFbo) {
- blit(mFbo, mBlurredFbo);
- }
-
- // Cleanup
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
- return NO_ERROR;
-}
-
-string KawaseBlurFilter::getFragmentShader() const {
- return R"SHADER(#version 310 es
- precision mediump float;
-
- uniform sampler2D uTexture;
- uniform vec2 uOffset;
-
- highp in vec2 vUV;
- out vec4 fragColor;
-
- void main() {
- fragColor = texture(uTexture, vUV, 0.0);
- fragColor += texture(uTexture, vUV + vec2( uOffset.x, uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2(-uOffset.x, uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);
-
- fragColor = vec4(fragColor.rgb * 0.2, 1.0);
- }
- )SHADER";
-}
-
-void KawaseBlurFilter::blit(GLFramebuffer& read, GLFramebuffer& draw) const {
- read.bindAsReadBuffer();
- draw.bindAsDrawBuffer();
- glBlitFramebuffer(0, 0, read.getBufferWidth(), read.getBufferHeight(), 0, 0,
- draw.getBufferWidth(), draw.getBufferHeight(), GL_COLOR_BUFFER_BIT,
- GL_LINEAR);
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/KawaseBlurFilter.h b/libs/renderengine/gl/filters/KawaseBlurFilter.h
deleted file mode 100644
index 20009cf..0000000
--- a/libs/renderengine/gl/filters/KawaseBlurFilter.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <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 KawaseBlurFilter : public BlurFilter {
-public:
- static constexpr uint32_t kMaxPasses = 6;
-
- explicit KawaseBlurFilter(GLESRenderEngine& engine);
- status_t prepare() override;
- void allocateTextures() override;
-
-private:
- string getFragmentShader() const;
- void blit(GLFramebuffer& read, GLFramebuffer& draw) const;
-
- GLFramebuffer mFbo;
-
- GenericProgram mProgram;
- GLuint mPosLoc;
- GLuint mUvLoc;
- GLuint mTextureLoc;
- GLuint mOffsetLoc;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 30c48c8..6fd4b80 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -364,7 +364,7 @@
}
*outYcbcr = ycbcr;
- return static_cast<status_t>(Error::UNSUPPORTED);
+ return static_cast<status_t>(Error::NONE);
}
int Gralloc4Mapper::unlock(buffer_handle_t bufferHandle) const {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 08d7d3f..c6f1f7e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -52,6 +52,7 @@
"libpdx_default_transport",
"libprocessgroup",
"libprotobuf-cpp-lite",
+ "libstatslog",
"libsync",
"libtimestats",
"libui",
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b876ccd..1765d2d 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -107,6 +107,12 @@
return *mAvailableRefreshRates.back();
}
+ // If there are not layers, there is not content detection, so return the current
+ // refresh rate.
+ if (layers.empty()) {
+ return getCurrentRefreshRateByPolicyLocked();
+ }
+
int noVoteLayers = 0;
int minVoteLayers = 0;
int maxVoteLayers = 0;
@@ -272,6 +278,10 @@
const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
+ return getCurrentRefreshRateByPolicyLocked();
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicyLocked() const {
if (std::find(mAvailableRefreshRates.begin(), mAvailableRefreshRates.end(),
mCurrentRefreshRate) != mAvailableRefreshRates.end()) {
return *mCurrentRefreshRate;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 0b5c73c..c8aec86 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -199,6 +199,10 @@
// display refresh period.
std::pair<nsecs_t, nsecs_t> getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const;
+ // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
+ // the policy.
+ const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock);
+
// The list of refresh rates, indexed by display config ID. This must not change after this
// object is initialized.
AllRefreshRatesMapType mRefreshRates;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 3a44332..5444239 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -99,12 +99,14 @@
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
const scheduler::RefreshRateConfigs& refreshRateConfig,
- ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
+ ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
+ bool useContentDetection)
: mPrimaryDispSync(createDispSync()),
mEventControlThread(new impl::EventControlThread(std::move(function))),
mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(refreshRateConfig),
+ mUseContentDetection(useContentDetection),
mUseContentDetectionV2(useContentDetectionV2) {
using namespace sysprop;
@@ -147,12 +149,14 @@
Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs,
- ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
+ ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
+ bool useContentDetection)
: mPrimaryDispSync(std::move(primaryDispSync)),
mEventControlThread(std::move(eventControlThread)),
mSupportKernelTimer(false),
mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(configs),
+ mUseContentDetection(useContentDetection),
mUseContentDetectionV2(useContentDetectionV2) {}
Scheduler::~Scheduler() {
@@ -381,6 +385,17 @@
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
+ // If the content detection feature is off, all layers are registered at NoVote. We still
+ // keep the layer history, since we use it for other features (like Frame Rate API), so layers
+ // still need to be registered.
+ if (!mUseContentDetection) {
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+ mRefreshRateConfigs.getMaxRefreshRate().fps,
+ scheduler::LayerHistory::LayerVoteType::NoVote);
+ return;
+ }
+
+ // In V1 of content detection, all layers are registered as Heuristic (unless it's wallpaper).
if (!mUseContentDetectionV2) {
const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
@@ -391,6 +406,7 @@
scheduler::LayerHistory::LayerVoteType::Heuristic);
} else {
if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
+ // Running Wallpaper at Min is considered as part of content detection.
mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
mRefreshRateConfigs.getMaxRefreshRate().fps,
scheduler::LayerHistory::LayerVoteType::Min);
@@ -512,6 +528,8 @@
// need to update the DispSync model anyway.
disableHardwareVsync(false /* makeUnavailable */);
}
+
+ mSchedulerCallback.kernelTimerChanged(state == TimerState::Expired);
}
void Scheduler::idleTimerCallback(TimerState state) {
@@ -537,8 +555,10 @@
StringAppendF(&result, "+ Idle timer: %s\n",
mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
- StringAppendF(&result, "+ Touch timer: %s\n\n",
+ StringAppendF(&result, "+ Touch timer: %s\n",
mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
+ StringAppendF(&result, "+ Use content detection: %s\n\n",
+ sysprop::use_content_detection_for_refresh_rate(false) ? "on" : "off");
}
template <class T>
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 46d1a5e..04cc96a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -51,6 +51,7 @@
virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
scheduler::RefreshRateConfigEvent) = 0;
virtual void repaintEverythingForHWC() = 0;
+ virtual void kernelTimerChanged(bool expired) = 0;
};
class Scheduler {
@@ -63,7 +64,7 @@
Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
- bool useContentDetectionV2);
+ bool useContentDetectionV2, bool useContentDetection);
virtual ~Scheduler();
@@ -159,7 +160,7 @@
// Used by tests to inject mocks.
Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
- bool useContentDetectionV2);
+ bool useContentDetectionV2, bool useContentDetection);
std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
@@ -245,6 +246,9 @@
GUARDED_BY(mVsyncTimelineLock);
static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
+ // This variable indicates whether to use the content detection feature at all.
+ const bool mUseContentDetection;
+ // This variable indicates whether to use V2 version of the content detection.
const bool mUseContentDetectionV2;
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 236a30f..f3755f4 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -21,28 +21,17 @@
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <sys/types.h>
-#include <errno.h>
-#include <dlfcn.h>
+#include "SurfaceFlinger.h"
-#include <algorithm>
-#include <cinttypes>
-#include <cmath>
-#include <cstdint>
-#include <functional>
-#include <mutex>
-#include <optional>
-#include <unordered_map>
-
+#include <android/configuration.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/types.h>
+#include <android/hardware/power/1.0/IPower.h>
#include <android/native_window.h>
-
-#include <cutils/properties.h>
-#include <log/log.h>
-
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
-
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/Display.h>
@@ -52,10 +41,14 @@
#include <compositionengine/OutputLayer.h>
#include <compositionengine/RenderSurface.h>
#include <compositionengine/impl/OutputCompositionState.h>
+#include <configstore/Utils.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <dlfcn.h>
#include <dvr/vr_flinger.h>
+#include <errno.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
-
#include <gui/GuiConfig.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/IProducerListener.h>
@@ -64,7 +57,13 @@
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <input/IInputFlinger.h>
+#include <layerproto/LayerProtoParser.h>
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#include <private/gui/SyncFeatures.h>
#include <renderengine/RenderEngine.h>
+#include <statslog.h>
+#include <sys/types.h>
#include <ui/ColorSpace.h>
#include <ui/DebugUtils.h>
#include <ui/DisplayConfig.h>
@@ -81,8 +80,14 @@
#include <utils/Trace.h>
#include <utils/misc.h>
-#include <private/android_filesystem_config.h>
-#include <private/gui/SyncFeatures.h>
+#include <algorithm>
+#include <cinttypes>
+#include <cmath>
+#include <cstdint>
+#include <functional>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
#include "BufferLayer.h"
#include "BufferQueueLayer.h"
@@ -91,23 +96,19 @@
#include "Colorizer.h"
#include "ContainerLayer.h"
#include "DisplayDevice.h"
-#include "EffectLayer.h"
-#include "Layer.h"
-#include "LayerVector.h"
-#include "MonitoredProducer.h"
-#include "NativeWindowSurface.h"
-#include "RefreshRateOverlay.h"
-#include "StartPropertySetThread.h"
-#include "SurfaceFlinger.h"
-#include "SurfaceInterceptor.h"
-
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/FramebufferSurface.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
+#include "EffectLayer.h"
#include "Effects/Daltonizer.h"
#include "FrameTracer/FrameTracer.h"
+#include "Layer.h"
+#include "LayerVector.h"
+#include "MonitoredProducer.h"
+#include "NativeWindowSurface.h"
+#include "RefreshRateOverlay.h"
#include "RegionSamplingThread.h"
#include "Scheduler/DispSync.h"
#include "Scheduler/DispSyncSource.h"
@@ -116,23 +117,13 @@
#include "Scheduler/MessageQueue.h"
#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
+#include "StartPropertySetThread.h"
+#include "SurfaceFlingerProperties.h"
+#include "SurfaceInterceptor.h"
#include "TimeStats/TimeStats.h"
-
-#include <cutils/compiler.h>
-
#include "android-base/parseint.h"
#include "android-base/stringprintf.h"
-#include <android/configuration.h>
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/power/1.0/IPower.h>
-#include <configstore/Utils.h>
-
-#include <layerproto/LayerProtoParser.h>
-#include "SurfaceFlingerProperties.h"
-
namespace android {
using namespace std::string_literals;
@@ -217,6 +208,7 @@
const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sDump("android.permission.DUMP");
+const char* KERNEL_IDLE_TIMER_PROP = "vendor.display.enable_kernel_idle_timer";
// ---------------------------------------------------------------------------
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
@@ -335,6 +327,9 @@
property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
mGpuToCpuSupported = !atoi(value);
+ property_get("ro.build.type", value, "user");
+ mIsUserBuild = strcmp(value, "user") == 0;
+
property_get("debug.sf.showupdates", value, "0");
mDebugRegion = atoi(value);
@@ -1813,6 +1808,10 @@
if (frameMissed) {
mFrameMissedCount++;
mTimeStats->incrementMissedFrames();
+ if (mMissedFrameJankCount == 0) {
+ mMissedFrameJankStart = systemTime();
+ }
+ mMissedFrameJankCount++;
}
if (hwcFrameMissed) {
@@ -1831,6 +1830,40 @@
}
}
+ // Our jank window is always at least 100ms since we missed a
+ // frame...
+ static constexpr nsecs_t kMinJankyDuration =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+ // ...but if it's larger than 1s then we missed the trace cutoff.
+ static constexpr nsecs_t kMaxJankyDuration =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+ // If we're in a user build then don't push any atoms
+ if (!mIsUserBuild && mMissedFrameJankCount > 0) {
+ const auto displayDevice = getDefaultDisplayDeviceLocked();
+ // Only report jank when the display is on, as displays in DOZE
+ // power mode may operate at a different frame rate than is
+ // reported in their config, which causes noticeable (but less
+ // severe) jank.
+ if (displayDevice && displayDevice->getPowerMode() == HWC_POWER_MODE_NORMAL) {
+ const nsecs_t currentTime = systemTime();
+ const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
+ if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
+ ATRACE_NAME("Jank detected");
+ ALOGD("Detected janky event. Missed frames: %d", mMissedFrameJankCount);
+ const int32_t jankyDurationMillis = jankDuration / (1000 * 1000);
+ android::util::stats_write(android::util::DISPLAY_JANK_REPORTED,
+ jankyDurationMillis, mMissedFrameJankCount);
+ }
+
+ // We either reported a jank event or we missed the trace
+ // window, so clear counters here.
+ if (jankDuration > kMinJankyDuration) {
+ mMissedFrameJankCount = 0;
+ mMissedFrameJankStart = 0;
+ }
+ }
+ }
+
// Now that we're going to make it to the handleMessageTransaction()
// call below it's safe to call updateVrFlinger(), which will
// potentially trigger a display handoff.
@@ -5124,6 +5157,36 @@
mEventQueue->invalidate();
}
+void SurfaceFlinger::kernelTimerChanged(bool expired) {
+ static bool updateOverlay =
+ property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
+ if (!updateOverlay || !mRefreshRateOverlay) return;
+
+ // Update the overlay on the main thread to avoid race conditions with
+ // mRefreshRateConfigs->getCurrentRefreshRate()
+ postMessageAsync(new LambdaMessage([this, expired]() NO_THREAD_SAFETY_ANALYSIS {
+ if (mRefreshRateOverlay) {
+ const auto kernelTimerEnabled = property_get_bool(KERNEL_IDLE_TIMER_PROP, false);
+ const bool timerExpired = kernelTimerEnabled && expired;
+ const auto& current = [this]() {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ if (mDesiredActiveConfigChanged) {
+ return mRefreshRateConfigs->getRefreshRateFromConfigId(
+ mDesiredActiveConfig.configId);
+ }
+
+ return mRefreshRateConfigs->getCurrentRefreshRate();
+ }();
+ const auto& min = mRefreshRateConfigs->getMinRefreshRate();
+
+ if (current != min) {
+ mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
+ mEventQueue->invalidate();
+ }
+ }
+ }));
+}
+
// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
class WindowDisconnector {
public:
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9b16a28..477d7a5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -524,6 +524,8 @@
void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
// force full composition on all displays without resetting the scheduler idle timer.
void repaintEverythingForHWC() override;
+ // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
+ void kernelTimerChanged(bool expired) override;
/* ------------------------------------------------------------------------
* Message handling
*/
@@ -983,6 +985,7 @@
// constant members (no synchronization needed for access)
const nsecs_t mBootTime = systemTime();
bool mGpuToCpuSupported = false;
+ bool mIsUserBuild = true;
// Can only accessed from the main thread, these members
// don't need synchronization
@@ -1226,6 +1229,11 @@
// Flags to capture the state of Vsync in HWC
HWC2::Vsync mHWCVsyncState = HWC2::Vsync::Disable;
HWC2::Vsync mHWCVsyncPendingState = HWC2::Vsync::Disable;
+
+ // Fields tracking the current jank event: when it started and how many
+ // janky frames there are.
+ nsecs_t mMissedFrameJankStart = 0;
+ int32_t mMissedFrameJankCount = 0;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 033e542..ddd20a5 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -33,6 +33,7 @@
#include "NativeWindowSurface.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerDefaultFactory.h"
+#include "SurfaceFlingerProperties.h"
#include "SurfaceInterceptor.h"
#include "DisplayHardware/ComposerHal.h"
@@ -76,8 +77,8 @@
SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
ISchedulerCallback& schedulerCallback) {
return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback,
- property_get_bool("debug.sf.use_content_detection_v2",
- true));
+ property_get_bool("debug.sf.use_content_detection_v2", true),
+ sysprop::use_content_detection_for_refresh_rate(false));
}
std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 7e62513..e7e7f66 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -247,6 +247,31 @@
refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
}
+TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_noLayers) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs = std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/
+ HWC_CONFIG_ID_72);
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 72};
+
+ // If there are not layers, there is not content detection, so return the current
+ // refresh rate.
+ auto layers = std::vector<LayerRequirement>{};
+ EXPECT_EQ(expected72Config,
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/
+ false));
+
+ // Current refresh rate can always be changed.
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/
+ false));
+}
+
TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_60_90) {
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 52da34b..41b5d49 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -29,7 +29,7 @@
class TestableScheduler : public Scheduler, private ISchedulerCallback {
public:
TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
- : Scheduler([](bool) {}, configs, *this, useContentDetectionV2) {
+ : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {
if (mUseContentDetectionV2) {
mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
} else {
@@ -41,7 +41,7 @@
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
: Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
- useContentDetectionV2) {
+ useContentDetectionV2, true) {
if (mUseContentDetectionV2) {
mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
} else {
@@ -92,6 +92,7 @@
private:
void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
void repaintEverythingForHWC() override {}
+ void kernelTimerChanged(bool /*expired*/) override {}
};
} // namespace android