Merge "libbinder: don't export private ParcelValTypes.h" am: 31d204ce1f am: 1c1a28dd0a
am: 52761aa5e3
Change-Id: Iccf96901359956e9e59a524c39c1fe09b57059fb
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 40f6b43..8e762f1 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -46,6 +46,7 @@
static const char* hal_interfaces_to_dump[] {
"android.hardware.audio@2.0::IDevicesFactory",
"android.hardware.audio@4.0::IDevicesFactory",
+ "android.hardware.biometrics.face@1.0::IBiometricsFace",
"android.hardware.bluetooth@1.0::IBluetoothHci",
"android.hardware.camera.provider@2.4::ICameraProvider",
"android.hardware.drm@1.0::IDrmFactory",
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9c311a3..92ab410 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -936,6 +936,15 @@
}
}
+ // Make sure to merge the damage rect from the frame we're about
+ // to drop into the new frame's damage rect.
+ if (last.mSurfaceDamage.bounds() == Rect::INVALID_RECT ||
+ item.mSurfaceDamage.bounds() == Rect::INVALID_RECT) {
+ item.mSurfaceDamage = Region::INVALID_REGION;
+ } else {
+ item.mSurfaceDamage |= last.mSurfaceDamage;
+ }
+
// Overwrite the droppable buffer with the incoming one
mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
frameReplacedListener = mCore->mConsumerListener;
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index f5cf1c4..b8faa2d 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -32,10 +32,11 @@
// ---------------------------------------------------------------------------
-DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) {
+DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::ConfigChanged configChanged) {
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != nullptr) {
- mEventConnection = sf->createDisplayEventConnection(vsyncSource);
+ mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
if (mEventConnection != nullptr) {
mDataChannel = std::make_unique<gui::BitTube>();
mEventConnection->stealReceiveChannel(mDataChannel.get());
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 6c9d81a..e487792 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -278,8 +278,8 @@
return NO_ERROR;
}
- virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource)
- {
+ virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource,
+ ConfigChanged configChanged) {
Parcel data, reply;
sp<IDisplayEventConnection> result;
int err = data.writeInterfaceToken(
@@ -288,6 +288,7 @@
return result;
}
data.writeInt32(static_cast<int32_t>(vsyncSource));
+ data.writeInt32(static_cast<int32_t>(configChanged));
err = remote()->transact(
BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
data, &reply);
@@ -1155,8 +1156,11 @@
}
case CREATE_DISPLAY_EVENT_CONNECTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IDisplayEventConnection> connection(createDisplayEventConnection(
- static_cast<ISurfaceComposer::VsyncSource>(data.readInt32())));
+ auto vsyncSource = static_cast<ISurfaceComposer::VsyncSource>(data.readInt32());
+ auto configChanged = static_cast<ISurfaceComposer::ConfigChanged>(data.readInt32());
+
+ sp<IDisplayEventConnection> connection(
+ createDisplayEventConnection(vsyncSource, configChanged));
reply->writeStrongBinder(IInterface::asBinder(connection));
return NO_ERROR;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index e6eb327..9fe5de8 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1920,7 +1920,8 @@
return OK;
}
-status_t Surface::attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffer) {
+status_t Surface::attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer,
+ Dataspace dataspace) {
if (buffer == nullptr) {
return BAD_VALUE;
}
@@ -1929,6 +1930,11 @@
if (err != OK) {
return err;
}
+ ui::Dataspace tmpDataspace = surface->getBuffersDataSpace();
+ err = surface->setBuffersDataSpace(dataspace);
+ if (err != OK) {
+ return err;
+ }
err = surface->attachBuffer(buffer->getNativeBuffer());
if (err != OK) {
return err;
@@ -1937,6 +1943,10 @@
if (err != OK) {
return err;
}
+ err = surface->setBuffersDataSpace(tmpDataspace);
+ if (err != OK) {
+ return err;
+ }
err = surface->disconnect(NATIVE_WINDOW_API_CPU);
return err;
}
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 22de751..a558cf9 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -88,10 +88,13 @@
* DisplayEventReceiver creates and registers an event connection with
* SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate
* or requestNextVsync to receive them.
+ * To receive Config Changed events specify this in the constructor.
* Other events start being delivered immediately.
*/
explicit DisplayEventReceiver(
- ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp);
+ ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
+ ISurfaceComposer::ConfigChanged configChanged =
+ ISurfaceComposer::eConfigChangedSuppress);
/*
* ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index e2f7736..c84910b 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -90,6 +90,8 @@
eVsyncSourceSurfaceFlinger = 1
};
+ enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 };
+
/*
* Create a connection with SurfaceFlinger.
*/
@@ -97,7 +99,8 @@
/* return an IDisplayEventConnection */
virtual sp<IDisplayEventConnection> createDisplayEventConnection(
- VsyncSource vsyncSource = eVsyncSourceApp) = 0;
+ VsyncSource vsyncSource = eVsyncSourceApp,
+ ConfigChanged configChanged = eConfigChangedSuppress) = 0;
/* create a virtual display
* requires ACCESS_SURFACE_FLINGER permission.
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 0c471bb..5c6a1ee 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -292,7 +292,8 @@
ui::Dataspace getBuffersDataSpace();
- static status_t attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffer);
+ static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer,
+ ui::Dataspace dataspace);
protected:
enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 960cf18..d370858 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -548,8 +548,8 @@
}
sp<ISurfaceComposerClient> createConnection() override { return nullptr; }
- sp<IDisplayEventConnection> createDisplayEventConnection(ISurfaceComposer::VsyncSource)
- override {
+ sp<IDisplayEventConnection> createDisplayEventConnection(
+ ISurfaceComposer::VsyncSource, ISurfaceComposer::ConfigChanged) override {
return nullptr;
}
sp<IBinder> createDisplay(const String8& /*displayName*/,
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 46a8e9e..dd12b55 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -614,64 +614,29 @@
}
}
-status_t GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- return cacheExternalTextureBufferLocked(buffer);
-}
-
status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName,
const sp<GraphicBuffer>& buffer,
const sp<Fence>& bufferFence) {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- return bindExternalTextureBufferLocked(texName, buffer, bufferFence);
-}
-
-status_t GLESRenderEngine::cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) {
- if (buffer == nullptr) {
- return BAD_VALUE;
- }
-
ATRACE_CALL();
-
- if (mImageCache.count(buffer->getId()) > 0) {
- return NO_ERROR;
- }
-
- std::unique_ptr<Image> newImage = createImage();
-
- bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
- buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
- if (!created) {
- ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
- buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
- buffer->getPixelFormat());
- return NO_INIT;
- }
- mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
-
- return NO_ERROR;
-}
-
-status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
- const sp<GraphicBuffer>& buffer,
- const sp<Fence>& bufferFence) {
- ATRACE_CALL();
- status_t cacheResult = cacheExternalTextureBufferLocked(buffer);
+ status_t cacheResult = cacheExternalTextureBuffer(buffer);
if (cacheResult != NO_ERROR) {
return cacheResult;
}
- auto cachedImage = mImageCache.find(buffer->getId());
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ auto cachedImage = mImageCache.find(buffer->getId());
- if (cachedImage == mImageCache.end()) {
- // We failed creating the image if we got here, so bail out.
- bindExternalTextureImage(texName, *createImage());
- return NO_INIT;
+ if (cachedImage == mImageCache.end()) {
+ // We failed creating the image if we got here, so bail out.
+ bindExternalTextureImage(texName, *createImage());
+ return NO_INIT;
+ }
+
+ bindExternalTextureImage(texName, *cachedImage->second);
}
- bindExternalTextureImage(texName, *cachedImage->second);
-
// Wait for the new buffer to be ready.
if (bufferFence != nullptr && bufferFence->isValid()) {
if (GLExtensions::getInstance().hasWaitSync()) {
@@ -696,6 +661,45 @@
return NO_ERROR;
}
+status_t GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ if (mImageCache.count(buffer->getId()) > 0) {
+ // If there's already an image then fail fast here.
+ return NO_ERROR;
+ }
+ }
+ ATRACE_CALL();
+
+ // Create the image without holding a lock so that we don't block anything.
+ std::unique_ptr<Image> newImage = createImage();
+
+ bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
+ buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+ if (!created) {
+ ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+ buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
+ buffer->getPixelFormat());
+ return NO_INIT;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ if (mImageCache.count(buffer->getId()) > 0) {
+ // In theory it's possible for another thread to recache the image,
+ // so bail out if another thread won.
+ return NO_ERROR;
+ }
+ mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
+ }
+
+ return NO_ERROR;
+}
+
void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
std::lock_guard<std::mutex> lock(mRenderingMutex);
const auto& cachedImage = mImageCache.find(bufferId);
@@ -889,127 +893,123 @@
return BAD_VALUE;
}
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
+ BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache);
- BindNativeBufferAsFramebuffer fbo(*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();
- }
-
- // clear the entire buffer, sometimes when we reuse buffers we'd persist
- // ghost images otherwise.
- // we also require a full transparent framebuffer for overlays. This is
- // probably not quite efficient on all GPUs, since we could filter out
- // opaque layers.
- clearWithColor(0.0, 0.0, 0.0, 0.0);
-
- setViewportAndProjection(display.physicalDisplay, display.clip);
-
- setOutputDataSpace(display.outputDataspace);
- setDisplayMaxLuminance(display.maxLuminance);
-
- mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
- mState.projectionMatrix = projectionMatrix;
- if (!display.clearRegion.isEmpty()) {
- glDisable(GL_BLEND);
- fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
- }
-
- Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
- for (auto layer : layers) {
- mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
-
- const FloatRect bounds = layer.geometry.boundaries;
- Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
- position[0] = vec2(bounds.left, bounds.top);
- position[1] = vec2(bounds.left, bounds.bottom);
- position[2] = vec2(bounds.right, bounds.bottom);
- position[3] = vec2(bounds.right, bounds.top);
-
- setupLayerCropping(layer, mesh);
- setColorTransform(display.colorTransform * layer.colorTransform);
-
- bool usePremultipliedAlpha = true;
- bool disableTexture = true;
- bool isOpaque = false;
-
- if (layer.source.buffer.buffer != nullptr) {
- disableTexture = false;
- isOpaque = layer.source.buffer.isOpaque;
-
- sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
- bindExternalTextureBufferLocked(layer.source.buffer.textureName, gBuf,
- layer.source.buffer.fence);
-
- usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
- Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
- mat4 texMatrix = layer.source.buffer.textureTransform;
-
- texture.setMatrix(texMatrix.asArray());
- texture.setFiltering(layer.source.buffer.useTextureFiltering);
-
- texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
- setSourceY410BT2020(layer.source.buffer.isY410BT2020);
-
- renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
- texCoords[0] = vec2(0.0, 0.0);
- texCoords[1] = vec2(0.0, 1.0);
- texCoords[2] = vec2(1.0, 1.0);
- texCoords[3] = vec2(1.0, 0.0);
- setupLayerTexturing(texture);
- }
-
- const half3 solidColor = layer.source.solidColor;
- const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
- // Buffer sources will have a black solid color ignored in the shader,
- // so in that scenario the solid color passed here is arbitrary.
- setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
- layer.geometry.roundedCornersRadius);
- if (layer.disableBlending) {
- glDisable(GL_BLEND);
- }
- setSourceDataSpace(layer.sourceDataspace);
-
- // We only want to do a special handling for rounded corners when having rounded corners
- // is the only reason it needs to turn on blending, otherwise, we handle it like the
- // usual way since it needs to turn on blending anyway.
- if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
- handleRoundedCorners(display, layer, mesh);
- } else {
- drawMesh(mesh);
- }
-
- // Cleanup if there's a buffer source
- if (layer.source.buffer.buffer != nullptr) {
- disableBlending();
- setSourceY410BT2020(false);
- disableTexturing();
- }
- }
-
- if (drawFence != nullptr) {
- *drawFence = flush();
- }
- // If flush failed or we don't support native fences, we need to force the
- // gl command stream to be executed.
- if (drawFence == nullptr || drawFence->get() < 0) {
- bool success = finish();
- if (!success) {
- ALOGE("Failed to flush RenderEngine commands");
- checkErrors();
- // Chances are, something illegal happened (either the caller passed
- // us bad parameters, or we messed up our shader generation).
- return INVALID_OPERATION;
- }
- }
-
+ if (fbo.getStatus() != NO_ERROR) {
+ ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+ buffer->handle);
checkErrors();
+ return fbo.getStatus();
}
+
+ // clear the entire buffer, sometimes when we reuse buffers we'd persist
+ // ghost images otherwise.
+ // we also require a full transparent framebuffer for overlays. This is
+ // probably not quite efficient on all GPUs, since we could filter out
+ // opaque layers.
+ clearWithColor(0.0, 0.0, 0.0, 0.0);
+
+ setViewportAndProjection(display.physicalDisplay, display.clip);
+
+ setOutputDataSpace(display.outputDataspace);
+ setDisplayMaxLuminance(display.maxLuminance);
+
+ mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform;
+ mState.projectionMatrix = projectionMatrix;
+ if (!display.clearRegion.isEmpty()) {
+ glDisable(GL_BLEND);
+ fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
+ }
+
+ Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
+ for (auto layer : layers) {
+ mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
+
+ const FloatRect bounds = layer.geometry.boundaries;
+ Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+ position[0] = vec2(bounds.left, bounds.top);
+ position[1] = vec2(bounds.left, bounds.bottom);
+ position[2] = vec2(bounds.right, bounds.bottom);
+ position[3] = vec2(bounds.right, bounds.top);
+
+ setupLayerCropping(layer, mesh);
+ setColorTransform(display.colorTransform * layer.colorTransform);
+
+ bool usePremultipliedAlpha = true;
+ bool disableTexture = true;
+ bool isOpaque = false;
+
+ if (layer.source.buffer.buffer != nullptr) {
+ disableTexture = false;
+ isOpaque = layer.source.buffer.isOpaque;
+
+ sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
+ bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
+ layer.source.buffer.fence);
+
+ usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
+ Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
+ mat4 texMatrix = layer.source.buffer.textureTransform;
+
+ texture.setMatrix(texMatrix.asArray());
+ texture.setFiltering(layer.source.buffer.useTextureFiltering);
+
+ texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
+ setSourceY410BT2020(layer.source.buffer.isY410BT2020);
+
+ renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
+ texCoords[0] = vec2(0.0, 0.0);
+ texCoords[1] = vec2(0.0, 1.0);
+ texCoords[2] = vec2(1.0, 1.0);
+ texCoords[3] = vec2(1.0, 0.0);
+ setupLayerTexturing(texture);
+ }
+
+ const half3 solidColor = layer.source.solidColor;
+ const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+ // Buffer sources will have a black solid color ignored in the shader,
+ // so in that scenario the solid color passed here is arbitrary.
+ setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color,
+ layer.geometry.roundedCornersRadius);
+ if (layer.disableBlending) {
+ glDisable(GL_BLEND);
+ }
+ setSourceDataSpace(layer.sourceDataspace);
+
+ // We only want to do a special handling for rounded corners when having rounded corners
+ // is the only reason it needs to turn on blending, otherwise, we handle it like the
+ // usual way since it needs to turn on blending anyway.
+ if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ handleRoundedCorners(display, layer, mesh);
+ } else {
+ drawMesh(mesh);
+ }
+
+ // Cleanup if there's a buffer source
+ if (layer.source.buffer.buffer != nullptr) {
+ disableBlending();
+ setSourceY410BT2020(false);
+ disableTexturing();
+ }
+ }
+
+ if (drawFence != nullptr) {
+ *drawFence = flush();
+ }
+ // If flush failed or we don't support native fences, we need to force the
+ // gl command stream to be executed.
+ if (drawFence == nullptr || drawFence->get() < 0) {
+ bool success = finish();
+ if (!success) {
+ ALOGE("Failed to flush RenderEngine commands");
+ checkErrors();
+ // Chances are, something illegal happened (either the caller passed
+ // us bad parameters, or we messed up our shader generation).
+ return INVALID_OPERATION;
+ }
+ }
+
+ checkErrors();
return NO_ERROR;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index de793c2..d6eab6c 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -85,8 +85,7 @@
bool useProtectedContext(bool useProtectedContext) override;
status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers,
ANativeWindowBuffer* buffer, const bool useFramebufferCache,
- base::unique_fd&& bufferFence, base::unique_fd* drawFence)
- EXCLUDES(mRenderingMutex) override;
+ base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
// internal to RenderEngine
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
@@ -220,15 +219,6 @@
// multiple threads is guaranteed thread-safe.
std::mutex mRenderingMutex;
- // See bindExternalTextureBuffer above, but requiring that mRenderingMutex
- // is held.
- status_t bindExternalTextureBufferLocked(uint32_t texName, const sp<GraphicBuffer>& buffer,
- const sp<Fence>& fence) REQUIRES(mRenderingMutex);
- // See cacheExternalTextureBuffer above, but requiring that mRenderingMutex
- // is held.
- status_t cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer)
- REQUIRES(mRenderingMutex);
-
std::unique_ptr<Framebuffer> mDrawingBuffer;
class FlushTracer {
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index e707004..f92ccfb 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -176,6 +176,17 @@
// should be called for every display that needs to be rendered via the GPU.
// @param display The display-wide settings that should be applied prior to
// drawing any layers.
+ //
+ // Assumptions when calling this method:
+ // 1. There is exactly one caller - i.e. multi-threading is not supported.
+ // 2. Additional threads may be calling the {bind,cache}ExternalTexture
+ // methods above. But the main thread is responsible for holding resources
+ // such that Image destruction does not occur while this method is called.
+ //
+ // TODO(b/136806342): This should behavior should ideally be fixed since
+ // the above two assumptions are brittle, as conditional thread safetyness
+ // may be insufficient when maximizing rendering performance in the future.
+ //
// @param layers The layers to draw onto the display, in Z-order.
// @param buffer The buffer which will be drawn to. This buffer will be
// ready once drawFence fires.
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index ef1a224..6a7f279 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -82,7 +82,7 @@
// Check if the "deep touch" feature is on.
static bool deepPressEnabled() {
std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
- INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
+ INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "false");
std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
if (flag_value == "1" || flag_value == "true") {
ALOGI("Deep press feature enabled.");
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 9aa4e85..5b298b4 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -153,9 +153,10 @@
"Scheduler/LayerHistory.cpp",
"Scheduler/LayerInfo.cpp",
"Scheduler/MessageQueue.cpp",
+ "Scheduler/PhaseOffsets.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
- "Scheduler/PhaseOffsets.cpp",
+ "Scheduler/VSyncModulator.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceInterceptor.cpp",
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index bd0b55f..d685366 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -143,7 +143,7 @@
}
Mutex::Autolock lock(mQueueItemLock);
- return mQueueItems[0].mTimestamp <= mFlinger->mScheduler->expectedPresentTime();
+ return mQueueItems[0].mTimestamp <= mFlinger->getExpectedPresentTime();
}
nsecs_t BufferQueueLayer::getDesiredPresentTime() {
@@ -201,7 +201,7 @@
uint64_t frameNumber = mQueueItems[0].mFrameNumber;
// The head of the queue will be dropped if there are signaled and timely frames behind it
- nsecs_t expectedPresentTime = mFlinger->mScheduler->expectedPresentTime();
+ nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime();
if (isRemovedFromCurrentState()) {
expectedPresentTime = 0;
@@ -279,7 +279,7 @@
getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode,
getTransformToDisplayInverse(), mFreezeGeometryUpdates);
- nsecs_t expectedPresentTime = mFlinger->mScheduler->expectedPresentTime();
+ nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime();
if (isRemovedFromCurrentState()) {
expectedPresentTime = 0;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 05c721f..2abc1a7 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -50,12 +50,6 @@
mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
}
-BufferStateLayer::~BufferStateLayer() {
- if (mActiveBuffer != nullptr) {
- auto& engine(mFlinger->getRenderEngine());
- engine.unbindExternalTextureBuffer(mActiveBuffer->getId());
- }
-}
// -----------------------------------------------------------------------
// Interface implementation for Layer
@@ -380,7 +374,7 @@
return true;
}
- return mDesiredPresentTime <= mFlinger->mScheduler->expectedPresentTime();
+ return mDesiredPresentTime <= mFlinger->getExpectedPresentTime();
}
nsecs_t BufferStateLayer::getDesiredPresentTime() {
@@ -571,11 +565,6 @@
return BAD_VALUE;
}
- if (mActiveBuffer != nullptr) {
- // todo: get this to work with BufferStateLayerCache
- auto& engine(mFlinger->getRenderEngine());
- engine.unbindExternalTextureBuffer(mActiveBuffer->getId());
- }
mActiveBuffer = s.buffer;
mActiveBufferFence = s.acquireFence;
auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
@@ -621,6 +610,10 @@
}
}
+void BufferStateLayer::bufferErased(const client_cache_t& clientCacheId) {
+ mFlinger->getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id);
+}
+
void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
std::lock_guard lock(mMutex);
if (!clientCacheId.isValid()) {
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 4e2bc45..db8ae0d 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -34,7 +34,6 @@
class BufferStateLayer : public BufferLayer {
public:
explicit BufferStateLayer(const LayerCreationArgs&);
- ~BufferStateLayer() override;
// -----------------------------------------------------------------------
// Interface implementation for Layer
@@ -103,6 +102,9 @@
bool fenceHasSignaled() const override;
bool framePresentTimeIsCurrent() const override;
+ // Inherit from ClientCache::ErasedRecipient
+ void bufferErased(const client_cache_t& clientCacheId) override;
+
private:
nsecs_t getDesiredPresentTime() override;
std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index cc5a5b5..7f47a2e 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -24,6 +24,7 @@
#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
#include <gui/BufferQueue.h>
+#include <hidl/HidlTransportSupport.h>
#include <hidl/HidlTransportUtils.h>
namespace android {
@@ -229,6 +230,7 @@
void Composer::registerCallback(const sp<IComposerCallback>& callback)
{
+ android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
auto ret = mClient->registerCallback(callback);
if (!ret.isOk()) {
ALOGE("failed to register IComposerCallback");
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8a80e15..ec7389e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -17,8 +17,6 @@
#ifndef ANDROID_LAYER_H
#define ANDROID_LAYER_H
-#include <sys/types.h>
-
#include <compositionengine/LayerFE.h>
#include <gui/BufferQueue.h>
#include <gui/ISurfaceComposerClient.h>
@@ -28,6 +26,7 @@
#include <math/vec4.h>
#include <renderengine/Mesh.h>
#include <renderengine/Texture.h>
+#include <sys/types.h>
#include <ui/FloatRect.h>
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
@@ -44,16 +43,16 @@
#include <vector>
#include "Client.h"
+#include "ClientCache.h"
+#include "DisplayHardware/ComposerHal.h"
+#include "DisplayHardware/HWComposer.h"
#include "FrameTracker.h"
#include "LayerVector.h"
#include "MonitoredProducer.h"
+#include "RenderArea.h"
#include "SurfaceFlinger.h"
#include "TransactionCompletedThread.h"
-#include "DisplayHardware/ComposerHal.h"
-#include "DisplayHardware/HWComposer.h"
-#include "RenderArea.h"
-
using namespace android::surfaceflinger;
namespace android {
@@ -94,7 +93,7 @@
LayerMetadata metadata;
};
-class Layer : public virtual compositionengine::LayerFE {
+class Layer : public virtual compositionengine::LayerFE, public ClientCache::ErasedRecipient {
static std::atomic<int32_t> sSequence;
public:
@@ -701,6 +700,9 @@
compositionengine::OutputLayer* findOutputLayerForDisplay(
const sp<const DisplayDevice>& display) const;
+ // Inherit from ClientCache::ErasedRecipient
+ void bufferErased(const client_cache_t& /*clientCacheId*/) override {}
+
protected:
// constant
sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 66906e9..07fdead 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -44,11 +44,14 @@
enum class samplingStep {
noWorkNeeded,
idleTimerWaiting,
+ waitForQuietFrame,
waitForZeroPhase,
waitForSamplePhase,
sample
};
+constexpr auto timeForRegionSampling = 5000000ns;
+constexpr auto maxRegionSamplingSkips = 10;
constexpr auto defaultRegionSamplingOffset = -3ms;
constexpr auto defaultRegionSamplingPeriod = 100ms;
constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
@@ -215,9 +218,9 @@
void RegionSamplingThread::checkForStaleLuma() {
std::lock_guard lock(mThreadControlMutex);
- if (mDiscardedFrames) {
+ if (mDiscardedFrames > 0) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
- mDiscardedFrames = false;
+ mDiscardedFrames = 0;
mPhaseCallback->startVsyncListener();
}
}
@@ -235,13 +238,25 @@
auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
if (lastSampleTime + mTunables.mSamplingPeriod > now) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
- mDiscardedFrames = true;
+ if (mDiscardedFrames == 0) mDiscardedFrames++;
return;
}
+ if (mDiscardedFrames < maxRegionSamplingSkips) {
+ // If there is relatively little time left for surfaceflinger
+ // until the next vsync deadline, defer this sampling work
+ // to a later frame, when hopefully there will be more time.
+ DisplayStatInfo stats;
+ mScheduler.getDisplayStatInfo(&stats);
+ if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
+ mDiscardedFrames++;
+ return;
+ }
+ }
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
- mDiscardedFrames = false;
+ mDiscardedFrames = 0;
lastSampleTime = now;
mIdleTimer.reset();
@@ -258,7 +273,7 @@
namespace {
// Using Rec. 709 primaries
-float getLuma(float r, float g, float b) {
+inline float getLuma(float r, float g, float b) {
constexpr auto rec709_red_primary = 0.2126f;
constexpr auto rec709_green_primary = 0.7152f;
constexpr auto rec709_blue_primary = 0.0722f;
@@ -293,10 +308,10 @@
const uint32_t* rowBase = data + row * stride;
for (int32_t column = area.left; column < area.right; ++column) {
uint32_t pixel = rowBase[column];
- const float r = (pixel & 0xFF) / 255.0f;
- const float g = ((pixel >> 8) & 0xFF) / 255.0f;
- const float b = ((pixel >> 16) & 0xFF) / 255.0f;
- const uint8_t luma = std::round(getLuma(r, g, b) * 255.0f);
+ const float r = pixel & 0xFF;
+ const float g = (pixel >> 8) & 0xFF;
+ const float b = (pixel >> 16) & 0xFF;
+ const uint8_t luma = std::round(getLuma(r, g, b));
++brightnessBuckets[luma];
if (brightnessBuckets[luma] > majoritySampleNum) return luma / 255.0f;
}
@@ -342,9 +357,19 @@
}
const auto device = mFlinger.getDefaultDisplayDevice();
- const auto display = device->getCompositionDisplay();
- const auto state = display->getState();
- const auto orientation = static_cast<ui::Transform::orientation_flags>(state.orientation);
+ const auto orientation = [](uint32_t orientation) {
+ switch (orientation) {
+ default:
+ case DisplayState::eOrientationDefault:
+ return ui::Transform::ROT_0;
+ case DisplayState::eOrientation90:
+ return ui::Transform::ROT_90;
+ case DisplayState::eOrientation180:
+ return ui::Transform::ROT_180;
+ case DisplayState::eOrientation270:
+ return ui::Transform::ROT_270;
+ }
+ }(device->getOrientation());
std::vector<RegionSamplingThread::Descriptor> descriptors;
Region sampleRegion;
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 3c6fcf3..96ffe20 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -117,7 +117,7 @@
std::condition_variable_any mCondition;
bool mRunning GUARDED_BY(mThreadControlMutex) = true;
bool mSampleRequested GUARDED_BY(mThreadControlMutex) = false;
- bool mDiscardedFrames GUARDED_BY(mThreadControlMutex) = false;
+ uint32_t mDiscardedFrames GUARDED_BY(mThreadControlMutex) = 0;
std::chrono::nanoseconds lastSampleTime GUARDED_BY(mThreadControlMutex);
std::mutex mSamplingMutex;
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index cd6fa41..83fd42b 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -79,11 +79,7 @@
Mutex::Autolock lock(mMutex);
mPhase = phase;
- if (mReferenceTime != referenceTime) {
- for (auto& eventListener : mEventListeners) {
- eventListener.mHasFired = false;
- }
- }
+ const bool referenceTimeChanged = mReferenceTime != referenceTime;
mReferenceTime = referenceTime;
if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) {
// Inflate the reference time to be the most recent predicted
@@ -94,6 +90,16 @@
mReferenceTime = mReferenceTime + (numOldPeriods)*mPeriod;
}
mPeriod = period;
+ if (!mModelLocked && referenceTimeChanged) {
+ for (auto& eventListener : mEventListeners) {
+ eventListener.mLastEventTime = mReferenceTime + mPhase + eventListener.mPhase;
+ // If mLastEventTime is after mReferenceTime (can happen when positive phase offsets
+ // are used) we treat it as like it happened in previous period.
+ if (eventListener.mLastEventTime > mReferenceTime) {
+ eventListener.mLastEventTime -= mPeriod;
+ }
+ }
+ }
if (mTraceDetailedInfo) {
ATRACE_INT64("DispSync:Period", mPeriod);
ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
@@ -115,11 +121,13 @@
void lockModel() {
Mutex::Autolock lock(mMutex);
mModelLocked = true;
+ ATRACE_INT("DispSync:ModelLocked", mModelLocked);
}
void unlockModel() {
Mutex::Autolock lock(mMutex);
mModelLocked = false;
+ ATRACE_INT("DispSync:ModelLocked", mModelLocked);
}
virtual bool threadLoop() {
@@ -284,12 +292,8 @@
// new offset to allow for a seamless offset change without double-firing or
// skipping.
nsecs_t diff = oldPhase - phase;
- if (diff > mPeriod / 2) {
- diff -= mPeriod;
- } else if (diff < -mPeriod / 2) {
- diff += mPeriod;
- }
eventListener.mLastEventTime -= diff;
+ eventListener.mLastCallbackTime -= diff;
mCond.signal();
return NO_ERROR;
}
@@ -304,7 +308,6 @@
nsecs_t mLastEventTime;
nsecs_t mLastCallbackTime;
DispSync::Callback* mCallback;
- bool mHasFired = false;
};
struct CallbackInvocation {
@@ -352,12 +355,7 @@
eventListener.mName);
continue;
}
- if (eventListener.mHasFired && !mModelLocked) {
- eventListener.mLastEventTime = t;
- ALOGV("[%s] [%s] Skipping event due to already firing", mName,
- eventListener.mName);
- continue;
- }
+
CallbackInvocation ci;
ci.mCallback = eventListener.mCallback;
ci.mEventTime = t;
@@ -366,7 +364,6 @@
callbackInvocations.push_back(ci);
eventListener.mLastEventTime = t;
eventListener.mLastCallbackTime = now;
- eventListener.mHasFired = true;
}
}
@@ -493,7 +490,6 @@
ALOGE("Couldn't set SCHED_FIFO for DispSyncThread");
}
- reset();
beginResync();
if (mTraceDetailedInfo && kEnableZeroPhaseTracer) {
@@ -545,17 +541,15 @@
void DispSync::beginResync() {
Mutex::Autolock lock(mMutex);
ALOGV("[%s] beginResync", mName);
- mThread->unlockModel();
- mModelUpdated = false;
- mNumResyncSamples = 0;
+ resetLocked();
}
-bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodChanged) {
+bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
Mutex::Autolock lock(mMutex);
ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
- *periodChanged = false;
+ *periodFlushed = false;
const size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
mResyncSamples[idx] = timestamp;
if (mNumResyncSamples == 0) {
@@ -569,16 +563,20 @@
const nsecs_t lastTimestamp = mResyncSamples[priorIdx];
const nsecs_t observedVsync = std::abs(timestamp - lastTimestamp);
- if (std::abs(observedVsync - mPendingPeriod) < std::abs(observedVsync - mPeriod)) {
- // Observed vsync is closer to the pending period, so reset the
- // model and flush the pending period.
+ if (std::abs(observedVsync - mPendingPeriod) <= std::abs(observedVsync - mIntendedPeriod)) {
+ // Either the observed vsync is closer to the pending period, (and
+ // thus we detected a period change), or the period change will
+ // no-op. In either case, reset the model and flush the pending
+ // period.
resetLocked();
+ mIntendedPeriod = mPendingPeriod;
mPeriod = mPendingPeriod;
mPendingPeriod = 0;
if (mTraceDetailedInfo) {
ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod);
+ ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod);
}
- *periodChanged = true;
+ *periodFlushed = true;
}
}
// Always update the reference time with the most recent timestamp.
@@ -609,6 +607,7 @@
bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0;
ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked");
if (modelLocked) {
+ *periodFlushed = true;
mThread->lockModel();
}
return !modelLocked;
@@ -643,10 +642,17 @@
void DispSync::setPeriod(nsecs_t period) {
Mutex::Autolock lock(mMutex);
- if (mTraceDetailedInfo) {
- ATRACE_INT("DispSync:PendingPeriod", period);
+
+ const bool pendingPeriodShouldChange =
+ period != mIntendedPeriod || (period == mIntendedPeriod && mPendingPeriod != 0);
+
+ if (pendingPeriodShouldChange) {
+ mPendingPeriod = period;
}
- mPendingPeriod = period;
+ if (mTraceDetailedInfo) {
+ ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod);
+ ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod);
+ }
}
nsecs_t DispSync::getPeriod() {
@@ -764,6 +770,9 @@
mPresentSampleOffset = 0;
mError = 0;
mZeroErrSamplesCount = 0;
+ if (mTraceDetailedInfo) {
+ ATRACE_INT64("DispSync:Error", mError);
+ }
for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
mPresentFences[i] = FenceTime::NO_FENCE;
}
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index 8f8b8e7..3e33c7e 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -49,7 +49,7 @@
virtual void reset() = 0;
virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
virtual void beginResync() = 0;
- virtual bool addResyncSample(nsecs_t timestamp, bool* periodChanged) = 0;
+ virtual bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) = 0;
virtual void endResync() = 0;
virtual void setPeriod(nsecs_t period) = 0;
virtual nsecs_t getPeriod() = 0;
@@ -120,17 +120,19 @@
// from the hardware vsync events.
void beginResync() override;
// Adds a vsync sample to the dispsync model. The timestamp is the time
- // of the vsync event that fired. periodChanged will return true if the
+ // of the vsync event that fired. periodFlushed will return true if the
// vsync period was detected to have changed to mPendingPeriod.
//
// This method will return true if more vsync samples are needed to lock
// down the DispSync model, and false otherwise.
- bool addResyncSample(nsecs_t timestamp, bool* periodChanged) override;
+ // periodFlushed will be set to true if mPendingPeriod is flushed to
+ // mIntendedPeriod, and false otherwise.
+ bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) override;
void endResync() override;
// The setPeriod method sets the vsync event model's period to a specific
- // value. This should be used to prime the model when a display is first
- // turned on. It should NOT be used after that.
+ // value. This should be used to prime the model when a display is first
+ // turned on, or when a refresh rate change is requested.
void setPeriod(nsecs_t period) override;
// The getPeriod method returns the current vsync period.
@@ -205,6 +207,11 @@
// nanoseconds.
nsecs_t mPeriod;
+ // mIntendedPeriod is the intended period of the modeled vsync events in
+ // nanoseconds. Under ideal conditions this should be similar if not the
+ // same as mPeriod, plus or minus an observed error.
+ nsecs_t mIntendedPeriod = 0;
+
// mPendingPeriod is the proposed period change in nanoseconds.
// If mPendingPeriod differs from mPeriod and is nonzero, it will
// be flushed to mPeriod when we detect that the hardware switched
@@ -236,8 +243,8 @@
// process to store information about the hardware vsync event times used
// to compute the model.
nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES] = {0};
- size_t mFirstResyncSample;
- size_t mNumResyncSamples;
+ size_t mFirstResyncSample = 0;
+ size_t mNumResyncSamples = 0;
int mNumResyncSamplesSincePresent;
// These member variables store information about the present fences used
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 00948ae..5faf46e 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -27,18 +27,23 @@
namespace android {
-DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
+DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset,
+ nsecs_t offsetThresholdForNextVsync, bool traceVsync,
const char* name)
: mName(name),
mTraceVsync(traceVsync),
mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
mVsyncEventLabel(base::StringPrintf("VSYNC-%s", name)),
+ mVsyncOffsetLabel(base::StringPrintf("VsyncOffset-%s", name)),
+ mVsyncNegativeOffsetLabel(base::StringPrintf("VsyncNegativeOffset-%s", name)),
mDispSync(dispSync),
- mPhaseOffset(phaseOffset) {}
+ mPhaseOffset(phaseOffset),
+ mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {}
void DispSyncSource::setVSyncEnabled(bool enable) {
std::lock_guard lock(mVsyncMutex);
if (enable) {
+ tracePhaseOffset();
status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
static_cast<DispSync::Callback*>(this),
mLastCallbackTime);
@@ -64,16 +69,21 @@
void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
std::lock_guard lock(mVsyncMutex);
-
- // Normalize phaseOffset to [0, period)
- auto period = mDispSync->getPeriod();
- phaseOffset %= period;
- if (phaseOffset < 0) {
- // If we're here, then phaseOffset is in (-period, 0). After this
- // operation, it will be in (0, period)
- phaseOffset += period;
+ const nsecs_t period = mDispSync->getPeriod();
+ // Check if offset should be handled as negative
+ if (phaseOffset >= mOffsetThresholdForNextVsync) {
+ phaseOffset -= period;
}
+
+ // Normalize phaseOffset to [-period, period)
+ const int numPeriods = phaseOffset / period;
+ phaseOffset -= numPeriods * period;
+ if (mPhaseOffset == phaseOffset) {
+ return;
+ }
+
mPhaseOffset = phaseOffset;
+ tracePhaseOffset();
// If we're not enabled, we don't need to mess with the listeners
if (!mEnabled) {
@@ -92,11 +102,11 @@
{
std::lock_guard lock(mCallbackMutex);
callback = mCallback;
+ }
- if (mTraceVsync) {
- mValue = (mValue + 1) % 2;
- ATRACE_INT(mVsyncEventLabel.c_str(), mValue);
- }
+ if (mTraceVsync) {
+ mValue = (mValue + 1) % 2;
+ ATRACE_INT(mVsyncEventLabel.c_str(), mValue);
}
if (callback != nullptr) {
@@ -104,4 +114,14 @@
}
}
-} // namespace android
\ No newline at end of file
+void DispSyncSource::tracePhaseOffset() {
+ if (mPhaseOffset > 0) {
+ ATRACE_INT(mVsyncOffsetLabel.c_str(), mPhaseOffset);
+ ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), 0);
+ } else {
+ ATRACE_INT(mVsyncOffsetLabel.c_str(), 0);
+ ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), -mPhaseOffset);
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 4759699..50560a5 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -25,7 +25,8 @@
class DispSyncSource final : public VSyncSource, private DispSync::Callback {
public:
- DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
+ DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync,
+ bool traceVsync, const char* name);
~DispSyncSource() override = default;
@@ -38,12 +39,16 @@
// The following method is the implementation of the DispSync::Callback.
virtual void onDispSyncEvent(nsecs_t when);
+ void tracePhaseOffset() REQUIRES(mVsyncMutex);
+
const char* const mName;
int mValue = 0;
const bool mTraceVsync;
const std::string mVsyncOnLabel;
const std::string mVsyncEventLabel;
+ const std::string mVsyncOffsetLabel;
+ const std::string mVsyncNegativeOffsetLabel;
nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0;
DispSync* mDispSync;
@@ -53,7 +58,8 @@
std::mutex mVsyncMutex;
nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex);
+ const nsecs_t mOffsetThresholdForNextVsync;
bool mEnabled GUARDED_BY(mVsyncMutex) = false;
};
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 05bad4d..9d1f777 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -76,6 +76,10 @@
return StringPrintf("VSync{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
", count=%u}",
event.header.displayId, event.vsync.count);
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+ return StringPrintf("ConfigChanged{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+ ", configId=%u}",
+ event.header.displayId, event.config.configId);
default:
return "Event{}";
}
@@ -107,8 +111,10 @@
} // namespace
EventThreadConnection::EventThreadConnection(EventThread* eventThread,
- ResyncCallback resyncCallback)
+ ResyncCallback resyncCallback,
+ ISurfaceComposer::ConfigChanged configChanged)
: resyncCallback(std::move(resyncCallback)),
+ configChanged(configChanged),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
@@ -203,8 +209,10 @@
mVSyncSource->setPhaseOffset(phaseOffset);
}
-sp<EventThreadConnection> EventThread::createEventConnection(ResyncCallback resyncCallback) const {
- return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback));
+sp<EventThreadConnection> EventThread::createEventConnection(
+ ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
+ return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
+ configChanged);
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -398,9 +406,11 @@
const sp<EventThreadConnection>& connection) const {
switch (event.header.type) {
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
- case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
return true;
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+ return connection->configChanged == ISurfaceComposer::eConfigChangedDispatch;
+
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
switch (connection->vsyncRequest) {
case VSyncRequest::None:
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 61530c6..dd23b88 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -69,7 +69,8 @@
class EventThreadConnection : public BnDisplayEventConnection {
public:
- EventThreadConnection(EventThread*, ResyncCallback);
+ EventThreadConnection(EventThread*, ResyncCallback,
+ ISurfaceComposer::ConfigChanged configChanged);
virtual ~EventThreadConnection();
virtual status_t postEvent(const DisplayEventReceiver::Event& event);
@@ -82,6 +83,7 @@
const ResyncCallback resyncCallback;
VSyncRequest vsyncRequest = VSyncRequest::None;
+ const ISurfaceComposer::ConfigChanged configChanged;
private:
virtual void onFirstRef();
@@ -93,7 +95,8 @@
public:
virtual ~EventThread();
- virtual sp<EventThreadConnection> createEventConnection(ResyncCallback) const = 0;
+ virtual sp<EventThreadConnection> createEventConnection(
+ ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const = 0;
// called before the screen is turned off from main thread
virtual void onScreenReleased() = 0;
@@ -128,7 +131,8 @@
EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback, const char* threadName);
~EventThread();
- sp<EventThreadConnection> createEventConnection(ResyncCallback) const override;
+ sp<EventThreadConnection> createEventConnection(
+ ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const override;
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 1db43a3..f80c233 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -46,11 +46,13 @@
LayerHistory::~LayerHistory() = default;
std::unique_ptr<LayerHistory::LayerHandle> LayerHistory::createLayer(const std::string name,
+ float minRefreshRate,
float maxRefreshRate) {
const int64_t id = sNextId++;
std::lock_guard lock(mLock);
- mInactiveLayerInfos.emplace(id, std::make_shared<LayerInfo>(name, maxRefreshRate));
+ mInactiveLayerInfos.emplace(id,
+ std::make_shared<LayerInfo>(name, minRefreshRate, maxRefreshRate));
return std::make_unique<LayerHistory::LayerHandle>(*this, id);
}
@@ -173,5 +175,18 @@
}
}
+void LayerHistory::clearHistory() {
+ std::lock_guard lock(mLock);
+
+ auto it = mActiveLayerInfos.begin();
+ while (it != mActiveLayerInfos.end()) {
+ auto id = it->first;
+ auto layerInfo = it->second;
+ layerInfo->clearHistory();
+ mInactiveLayerInfos.insert({id, layerInfo});
+ it = mActiveLayerInfos.erase(it);
+ }
+}
+
} // namespace scheduler
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index adc5ce5..5598cc1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -53,7 +53,8 @@
~LayerHistory();
// When the layer is first created, register it.
- std::unique_ptr<LayerHandle> createLayer(const std::string name, float maxRefreshRate);
+ std::unique_ptr<LayerHandle> createLayer(const std::string name, float minRefreshRate,
+ float maxRefreshRate);
// Method for inserting layers and their requested present time into the unordered map.
void insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime, bool isHdr);
@@ -64,6 +65,9 @@
// layers. See go/content-fps-detection-in-scheduler for more information.
std::pair<float, bool> getDesiredRefreshRateAndHDR();
+ // Clears all layer history.
+ void clearHistory();
+
// Removes the handle and the object from the map.
void destroyLayer(const int64_t id);
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 95d7d31..e782dd5 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -24,9 +24,10 @@
namespace android {
namespace scheduler {
-LayerInfo::LayerInfo(const std::string name, float maxRefreshRate)
+LayerInfo::LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate)
: mName(name),
mMinRefreshDuration(1e9f / maxRefreshRate),
+ mLowActivityRefreshDuration(1e9f / minRefreshRate),
mRefreshRateHistory(mMinRefreshDuration) {}
LayerInfo::~LayerInfo() = default;
@@ -38,12 +39,19 @@
mLastUpdatedTime = std::max(lastPresentTime, systemTime());
mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
+ if (mLastPresentTime == 0) {
+ // First frame
+ mLastPresentTime = lastPresentTime;
+ return;
+ }
+
const nsecs_t timeDiff = lastPresentTime - mLastPresentTime;
mLastPresentTime = lastPresentTime;
// Ignore time diff that are too high - those are stale values
- if (timeDiff > TIME_EPSILON_NS.count()) return;
+ if (timeDiff > OBSOLETE_TIME_EPSILON_NS.count()) return;
const nsecs_t refreshDuration = (timeDiff > 0) ? timeDiff : mMinRefreshDuration;
- mRefreshRateHistory.insertRefreshRate(refreshDuration);
+ const int fps = 1e9f / refreshDuration;
+ mRefreshRateHistory.insertRefreshRate(fps);
}
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 02b6aef..66df9dc 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -46,7 +46,7 @@
public:
explicit RefreshRateHistory(nsecs_t minRefreshDuration)
: mMinRefreshDuration(minRefreshDuration) {}
- void insertRefreshRate(nsecs_t refreshRate) {
+ void insertRefreshRate(int refreshRate) {
mElements.push_back(refreshRate);
if (mElements.size() > HISTORY_SIZE) {
mElements.pop_front();
@@ -54,13 +54,13 @@
}
float getRefreshRateAvg() const {
- nsecs_t refreshDuration = mMinRefreshDuration;
- if (mElements.size() == HISTORY_SIZE) {
- refreshDuration = scheduler::calculate_mean(mElements);
+ if (mElements.empty()) {
+ return 1e9f / mMinRefreshDuration;
}
- return 1e9f / refreshDuration;
+ return scheduler::calculate_mean(mElements);
}
+
void clearHistory() { mElements.clear(); }
private:
@@ -86,13 +86,42 @@
// Checks whether the present time that was inserted HISTORY_SIZE ago is within a
// certain threshold: TIME_EPSILON_NS.
bool isRelevant() const {
- const int64_t obsoleteEpsilon = systemTime() - scheduler::TIME_EPSILON_NS.count();
- // The layer had to publish at least HISTORY_SIZE of updates, and the first
- // update should not be older than TIME_EPSILON_NS nanoseconds.
- if (mElements.size() == HISTORY_SIZE &&
- mElements.at(HISTORY_SIZE - 1) > obsoleteEpsilon) {
+ if (mElements.size() < 2) {
+ return false;
+ }
+
+ // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+ if (mElements.size() != HISTORY_SIZE &&
+ mElements.at(mElements.size() - 1) - mElements.at(0) < HISTORY_TIME.count()) {
+ return false;
+ }
+
+ // The last update should not be older than OBSOLETE_TIME_EPSILON_NS nanoseconds.
+ const int64_t obsoleteEpsilon =
+ systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
+ if (mElements.at(mElements.size() - 1) < obsoleteEpsilon) {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool isLowActivityLayer() const {
+ // We want to make sure that we received more than two frames from the layer
+ // in order to check low activity.
+ if (mElements.size() < 2) {
+ return false;
+ }
+
+ const int64_t obsoleteEpsilon =
+ systemTime() - scheduler::LOW_ACTIVITY_EPSILON_NS.count();
+ // Check the frame before last to determine whether there is low activity.
+ // If that frame is older than LOW_ACTIVITY_EPSILON_NS, the layer is sending
+ // infrequent updates.
+ if (mElements.at(mElements.size() - 2) < obsoleteEpsilon) {
return true;
}
+
return false;
}
@@ -101,10 +130,11 @@
private:
std::deque<nsecs_t> mElements;
static constexpr size_t HISTORY_SIZE = 10;
+ static constexpr std::chrono::nanoseconds HISTORY_TIME = 500ms;
};
public:
- LayerInfo(const std::string name, float maxRefreshRate);
+ LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate);
~LayerInfo();
LayerInfo(const LayerInfo&) = delete;
@@ -134,6 +164,10 @@
// Calculate the average refresh rate.
float getDesiredRefreshRate() const {
std::lock_guard lock(mLock);
+
+ if (mPresentTimeHistory.isLowActivityLayer()) {
+ return 1e9f / mLowActivityRefreshDuration;
+ }
return mRefreshRateHistory.getRefreshRateAvg();
}
@@ -165,6 +199,7 @@
private:
const std::string mName;
const nsecs_t mMinRefreshDuration;
+ const nsecs_t mLowActivityRefreshDuration;
mutable std::mutex mLock;
nsecs_t mLastUpdatedTime GUARDED_BY(mLock) = 0;
nsecs_t mLastPresentTime GUARDED_BY(mLock) = 0;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index baf900d..fcb307f 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -96,7 +96,8 @@
}
mEventThread = eventThread;
- mEvents = eventThread->createEventConnection(std::move(resyncCallback));
+ mEvents = eventThread->createEventConnection(std::move(resyncCallback),
+ ISurfaceComposer::eConfigChangedSuppress);
mEvents->stealReceiveChannel(&mEventTube);
mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
this);
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 276bce1..8a2604f 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -25,6 +25,7 @@
namespace scheduler {
+using RefreshRateType = RefreshRateConfigs::RefreshRateType;
PhaseOffsets::~PhaseOffsets() = default;
namespace impl {
@@ -72,25 +73,32 @@
property_get("debug.sf.phase_offset_threshold_for_next_vsync_ns", value, "-1");
const int phaseOffsetThresholdForNextVsyncNs = atoi(value);
- mDefaultRefreshRateOffsets.early = {earlySfOffsetNs != -1 ? earlySfOffsetNs
- : sfVsyncPhaseOffsetNs,
- earlyAppOffsetNs != -1 ? earlyAppOffsetNs
- : vsyncPhaseOffsetNs};
- mDefaultRefreshRateOffsets.earlyGl = {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs
- : sfVsyncPhaseOffsetNs,
- earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs
- : vsyncPhaseOffsetNs};
- mDefaultRefreshRateOffsets.late = {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
+ Offsets defaultOffsets;
+ Offsets highFpsOffsets;
+ defaultOffsets.early = {RefreshRateType::DEFAULT,
+ earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs,
+ earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs};
+ defaultOffsets.earlyGl = {RefreshRateType::DEFAULT,
+ earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs,
+ earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs};
+ defaultOffsets.late = {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
- mHighRefreshRateOffsets.early = {highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
- : highFpsLateSfOffsetNs,
- highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
- : highFpsLateAppOffsetNs};
- mHighRefreshRateOffsets.earlyGl = {highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
- : highFpsLateSfOffsetNs,
- highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
- : highFpsLateAppOffsetNs};
- mHighRefreshRateOffsets.late = {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs};
+ highFpsOffsets.early = {RefreshRateType::PERFORMANCE,
+ highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
+ : highFpsLateSfOffsetNs,
+ highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
+ : highFpsLateAppOffsetNs};
+ highFpsOffsets.earlyGl = {RefreshRateType::PERFORMANCE,
+ highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
+ : highFpsLateSfOffsetNs,
+ highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
+ : highFpsLateAppOffsetNs};
+ highFpsOffsets.late = {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs,
+ highFpsLateAppOffsetNs};
+
+ mOffsets.insert({RefreshRateType::POWER_SAVING, defaultOffsets});
+ mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
+ mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});
mOffsetThresholdForNextVsync = phaseOffsetThresholdForNextVsyncNs != -1
? phaseOffsetThresholdForNextVsyncNs
@@ -99,12 +107,7 @@
PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(
android::scheduler::RefreshRateConfigs::RefreshRateType refreshRateType) const {
- switch (refreshRateType) {
- case RefreshRateConfigs::RefreshRateType::PERFORMANCE:
- return mHighRefreshRateOffsets;
- default:
- return mDefaultRefreshRateOffsets;
- }
+ return mOffsets.at(refreshRateType);
}
void PhaseOffsets::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index dc71e6e..2b5c2f1 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -17,6 +17,7 @@
#pragma once
#include <cinttypes>
+#include <unordered_map>
#include "RefreshRateConfigs.h"
#include "VSyncModulator.h"
@@ -79,14 +80,10 @@
void dump(std::string& result) const override;
private:
- Offsets getDefaultRefreshRateOffsets() { return mDefaultRefreshRateOffsets; }
- Offsets getHighRefreshRateOffsets() { return mHighRefreshRateOffsets; }
-
std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType =
RefreshRateConfigs::RefreshRateType::DEFAULT;
- Offsets mDefaultRefreshRateOffsets;
- Offsets mHighRefreshRateOffsets;
+ std::unordered_map<RefreshRateConfigs::RefreshRateType, Offsets> mOffsets;
nsecs_t mOffsetThresholdForNextVsync;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 513436a..a194106 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -76,6 +76,7 @@
mSupportKernelTimer = support_kernel_idle_timer(false);
mSetTouchTimerMs = set_touch_timer_ms(0);
+ mSetDisplayPowerTimerMs = set_display_power_timer_ms(0);
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.set_idle_timer_ms", value, "0");
@@ -110,26 +111,40 @@
[this] { expiredTouchTimerCallback(); });
mTouchTimer->start();
}
+
+ if (mSetDisplayPowerTimerMs > 0) {
+ mDisplayPowerTimer =
+ std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
+ mSetDisplayPowerTimerMs),
+ [this] { resetDisplayPowerTimerCallback(); },
+ [this] {
+ expiredDisplayPowerTimerCallback();
+ });
+ mDisplayPowerTimer->start();
+ }
}
Scheduler::~Scheduler() {
// Ensure the IdleTimer thread is joined before we start destroying state.
+ mDisplayPowerTimer.reset();
mTouchTimer.reset();
mIdleTimer.reset();
}
sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
- const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback,
+ const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
+ ResyncCallback resyncCallback,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
const int64_t id = sNextId++;
ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
std::unique_ptr<EventThread> eventThread =
makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
- std::move(interceptCallback));
+ offsetThresholdForNextVsync, std::move(interceptCallback));
auto eventThreadConnection =
- createConnectionInternal(eventThread.get(), std::move(resyncCallback));
+ createConnectionInternal(eventThread.get(), std::move(resyncCallback),
+ ISurfaceComposer::eConfigChangedSuppress);
mConnections.emplace(id,
std::make_unique<Connection>(new ConnectionHandle(id),
eventThreadConnection,
@@ -138,24 +153,28 @@
}
std::unique_ptr<EventThread> Scheduler::makeEventThread(
- const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+ const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs,
+ nsecs_t offsetThresholdForNextVsync,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
std::unique_ptr<VSyncSource> eventThreadSource =
- std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, connectionName);
+ std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, offsetThresholdForNextVsync,
+ true, connectionName);
return std::make_unique<impl::EventThread>(std::move(eventThreadSource),
std::move(interceptCallback), connectionName);
}
-sp<EventThreadConnection> Scheduler::createConnectionInternal(EventThread* eventThread,
- ResyncCallback&& resyncCallback) {
- return eventThread->createEventConnection(std::move(resyncCallback));
+sp<EventThreadConnection> Scheduler::createConnectionInternal(
+ EventThread* eventThread, ResyncCallback&& resyncCallback,
+ ISurfaceComposer::ConfigChanged configChanged) {
+ return eventThread->createEventConnection(std::move(resyncCallback), configChanged);
}
sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
- const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback) {
+ const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback,
+ ISurfaceComposer::ConfigChanged configChanged) {
RETURN_VALUE_IF_INVALID(nullptr);
return createConnectionInternal(mConnections[handle->id]->thread.get(),
- std::move(resyncCallback));
+ std::move(resyncCallback), configChanged);
}
EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
@@ -280,13 +299,13 @@
}
}
-void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodChanged) {
+void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodFlushed) {
bool needsHwVsync = false;
- *periodChanged = false;
+ *periodFlushed = false;
{ // Scope for the lock
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
- needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodChanged);
+ needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodFlushed);
}
}
@@ -309,7 +328,7 @@
mPrimaryDispSync->setIgnorePresentFences(ignore);
}
-nsecs_t Scheduler::expectedPresentTime() {
+nsecs_t Scheduler::getDispSyncExpectedPresentTime() {
return mPrimaryDispSync->expectedPresentTime();
}
@@ -324,8 +343,11 @@
: RefreshRateType::PERFORMANCE;
const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType);
- const uint32_t fps = (refreshRate) ? refreshRate->fps : 0;
- return mLayerHistory.createLayer(name, fps);
+ const uint32_t performanceFps = (refreshRate) ? refreshRate->fps : 0;
+
+ const auto defaultRefreshRate = mRefreshRateConfigs.getRefreshRate(RefreshRateType::DEFAULT);
+ const uint32_t defaultFps = (defaultRefreshRate) ? defaultRefreshRate->fps : 0;
+ return mLayerHistory.createLayer(name, defaultFps, performanceFps);
}
void Scheduler::addLayerPresentTimeAndHDR(
@@ -371,11 +393,17 @@
}
void Scheduler::setChangeRefreshRateCallback(
- const ChangeRefreshRateCallback& changeRefreshRateCallback) {
+ const ChangeRefreshRateCallback&& changeRefreshRateCallback) {
std::lock_guard<std::mutex> lock(mCallbackLock);
mChangeRefreshRateCallback = changeRefreshRateCallback;
}
+void Scheduler::setGetCurrentRefreshRateTypeCallback(
+ const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateTypeCallback) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ mGetCurrentRefreshRateTypeCallback = getCurrentRefreshRateTypeCallback;
+}
+
void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) {
std::lock_guard<std::mutex> lock(mCallbackLock);
mGetVsyncPeriod = getVsyncPeriod;
@@ -404,43 +432,81 @@
if (mSupportKernelTimer) {
resetIdleTimer();
}
+
+ // Touch event will boost the refresh rate to performance.
+ // Clear Layer History to get fresh FPS detection
+ mLayerHistory.clearHistory();
+}
+
+void Scheduler::setDisplayPowerState(bool normal) {
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mIsDisplayPowerStateNormal = normal;
+ }
+
+ if (mDisplayPowerTimer) {
+ mDisplayPowerTimer->reset();
+ }
+
+ // Display Power event will boost the refresh rate to performance.
+ // Clear Layer History to get fresh FPS detection
+ mLayerHistory.clearHistory();
}
void Scheduler::resetTimerCallback() {
- timerChangeRefreshRate(IdleTimerState::RESET);
+ handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::RESET, false);
ATRACE_INT("ExpiredIdleTimer", 0);
}
void Scheduler::resetKernelTimerCallback() {
ATRACE_INT("ExpiredKernelIdleTimer", 0);
std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mGetVsyncPeriod) {
- resyncToHardwareVsync(false, mGetVsyncPeriod());
+ if (mGetVsyncPeriod && mGetCurrentRefreshRateTypeCallback) {
+ // If we're not in performance mode then the kernel timer shouldn't do
+ // anything, as the refresh rate during DPU power collapse will be the
+ // same.
+ if (mGetCurrentRefreshRateTypeCallback() == Scheduler::RefreshRateType::PERFORMANCE) {
+ resyncToHardwareVsync(true, mGetVsyncPeriod());
+ }
}
}
void Scheduler::expiredTimerCallback() {
- timerChangeRefreshRate(IdleTimerState::EXPIRED);
+ handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::EXPIRED, false);
ATRACE_INT("ExpiredIdleTimer", 1);
}
void Scheduler::resetTouchTimerCallback() {
- // We do not notify the applications about config changes when idle timer is reset.
- touchChangeRefreshRate(TouchState::ACTIVE);
+ handleTimerStateChanged(&mCurrentTouchState, TouchState::ACTIVE, true);
ATRACE_INT("TouchState", 1);
}
void Scheduler::expiredTouchTimerCallback() {
- // We do not notify the applications about config changes when idle timer expires.
- touchChangeRefreshRate(TouchState::INACTIVE);
+ handleTimerStateChanged(&mCurrentTouchState, TouchState::INACTIVE, true);
ATRACE_INT("TouchState", 0);
}
+void Scheduler::resetDisplayPowerTimerCallback() {
+ handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::RESET, true);
+ ATRACE_INT("ExpiredDisplayPowerTimer", 0);
+}
+
+void Scheduler::expiredDisplayPowerTimerCallback() {
+ handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::EXPIRED, true);
+ ATRACE_INT("ExpiredDisplayPowerTimer", 1);
+}
+
void Scheduler::expiredKernelTimerCallback() {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
ATRACE_INT("ExpiredKernelIdleTimer", 1);
- // Disable HW Vsync if the timer expired, as we don't need it
- // enabled if we're not pushing frames.
- disableHardwareVsync(false);
+ if (mGetCurrentRefreshRateTypeCallback) {
+ if (mGetCurrentRefreshRateTypeCallback() != Scheduler::RefreshRateType::PERFORMANCE) {
+ // Disable HW Vsync if the timer expired, as we don't need it
+ // enabled if we're not pushing frames, and if we're in PERFORMANCE
+ // mode then we'll need to re-update the DispSync model anyways.
+ disableHardwareVsync(false);
+ }
+ }
}
std::string Scheduler::doDump() {
@@ -450,39 +516,23 @@
return stream.str();
}
-void Scheduler::timerChangeRefreshRate(IdleTimerState idleTimerState) {
- RefreshRateType newRefreshRateType;
- {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mCurrentIdleTimerState == idleTimerState) {
- return;
- }
- mCurrentIdleTimerState = idleTimerState;
- newRefreshRateType = calculateRefreshRateType();
- if (mRefreshRateType == newRefreshRateType) {
- return;
- }
- mRefreshRateType = newRefreshRateType;
- }
- changeRefreshRate(newRefreshRateType, ConfigEvent::None);
-}
-
-void Scheduler::touchChangeRefreshRate(TouchState touchState) {
+template <class T>
+void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
ConfigEvent event = ConfigEvent::None;
RefreshRateType newRefreshRateType;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mCurrentTouchState == touchState) {
+ if (*currentState == newState) {
return;
}
- mCurrentTouchState = touchState;
+ *currentState = newState;
newRefreshRateType = calculateRefreshRateType();
if (mRefreshRateType == newRefreshRateType) {
return;
}
mRefreshRateType = newRefreshRateType;
- // Send an event in case that content detection is on as touch has a higher priority
- if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
+ if (eventOnContentDetection &&
+ mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
event = ConfigEvent::Changed;
}
}
@@ -495,6 +545,12 @@
return RefreshRateType::DEFAULT;
}
+ // If Display Power is not in normal operation we want to be in performance mode.
+ // When coming back to normal mode, a grace period is given with DisplayPowerTimer
+ if (!mIsDisplayPowerStateNormal || mDisplayPowerTimerState == DisplayPowerTimerState::RESET) {
+ return RefreshRateType::PERFORMANCE;
+ }
+
// As long as touch is active we want to be in performance mode
if (mCurrentTouchState == TouchState::ACTIVE) {
return RefreshRateType::PERFORMANCE;
@@ -510,22 +566,19 @@
return RefreshRateType::PERFORMANCE;
}
- // Content detection is on, find the appropriate refresh rate
- // Start with the smallest refresh rate which is within a margin of the content
- RefreshRateType currRefreshRateType = RefreshRateType::PERFORMANCE;
- constexpr float MARGIN = 0.05f;
- auto iter = mRefreshRateConfigs.getRefreshRates().cbegin();
- while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
- if (iter->second->fps >= mContentRefreshRate * (1 - MARGIN)) {
- currRefreshRateType = iter->first;
- break;
- }
- ++iter;
- }
+ // Content detection is on, find the appropriate refresh rate with minimal error
+ auto iter = min_element(mRefreshRateConfigs.getRefreshRates().cbegin(),
+ mRefreshRateConfigs.getRefreshRates().cend(),
+ [rate = mContentRefreshRate](const auto& l, const auto& r) -> bool {
+ return std::abs(l.second->fps - static_cast<float>(rate)) <
+ std::abs(r.second->fps - static_cast<float>(rate));
+ });
+ RefreshRateType currRefreshRateType = iter->first;
// Some content aligns better on higher refresh rate. For example for 45fps we should choose
// 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
// align well with both
+ constexpr float MARGIN = 0.05f;
float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps /
float(mContentRefreshRate);
if (std::abs(std::round(ratio) - ratio) > MARGIN) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 96d4bd5..5d8bb4c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -49,6 +49,7 @@
}
using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+ using GetCurrentRefreshRateTypeCallback = std::function<RefreshRateType()>;
using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
using GetVsyncPeriod = std::function<nsecs_t()>;
@@ -96,12 +97,13 @@
virtual ~Scheduler();
/** Creates an EventThread connection. */
- sp<ConnectionHandle> createConnection(const char* connectionName, int64_t phaseOffsetNs,
- ResyncCallback,
+ sp<ConnectionHandle> createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
+ nsecs_t offsetThresholdForNextVsync, ResyncCallback,
impl::EventThread::InterceptVSyncsCallback);
- sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle,
- ResyncCallback);
+ sp<IDisplayEventConnection> createDisplayEventConnection(
+ const sp<ConnectionHandle>& handle, ResyncCallback,
+ ISurfaceComposer::ConfigChanged configChanged);
// Getter methods.
EventThread* getEventThread(const sp<ConnectionHandle>& handle);
@@ -135,16 +137,21 @@
void enableHardwareVsync();
void disableHardwareVsync(bool makeUnavailable);
+ // Resyncs the scheduler to hardware vsync.
+ // If makeAvailable is true, then hardware vsync will be turned on.
+ // Otherwise, if hardware vsync is not already enabled then this method will
+ // no-op.
+ // The period is the vsync period from the current display configuration.
void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
// Creates a callback for resyncing.
ResyncCallback makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod);
void setRefreshSkipCount(int count);
- // Passes a vsync sample to DispSync. periodChange will be true if DipSync
- // detected that the vsync period changed, and false otherwise.
- void addResyncSample(const nsecs_t timestamp, bool* periodChanged);
+ // Passes a vsync sample to DispSync. periodFlushed will be true if
+ // DispSync detected that the vsync period changed, and false otherwise.
+ void addResyncSample(const nsecs_t timestamp, bool* periodFlushed);
void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
void setIgnorePresentFences(bool ignore);
- nsecs_t expectedPresentTime();
+ nsecs_t getDispSyncExpectedPresentTime();
// Registers the layer in the scheduler, and returns the handle for future references.
std::unique_ptr<scheduler::LayerHistory::LayerHandle> registerLayer(std::string const& name,
int windowType);
@@ -159,7 +166,9 @@
// Updates FPS based on the most content presented.
void updateFpsBasedOnContent();
// Callback that gets invoked when Scheduler wants to change the refresh rate.
- void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
+ void setChangeRefreshRateCallback(const ChangeRefreshRateCallback&& changeRefreshRateCallback);
+ void setGetCurrentRefreshRateTypeCallback(
+ const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateType);
void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod);
// Returns whether idle timer is enabled or not
@@ -171,6 +180,9 @@
// Function that resets the touch timer.
void notifyTouchEvent();
+ // Function that sets whether display power mode is normal or not.
+ void setDisplayPowerState(bool normal);
+
// Returns relevant information about Scheduler for dumpsys purposes.
std::string doDump();
@@ -179,7 +191,8 @@
protected:
virtual std::unique_ptr<EventThread> makeEventThread(
- const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
+ const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs,
+ nsecs_t offsetThresholdForNextVsync,
impl::EventThread::InterceptVSyncsCallback interceptCallback);
private:
@@ -190,9 +203,11 @@
enum class ContentFeatureState { CONTENT_DETECTION_ON, CONTENT_DETECTION_OFF };
enum class IdleTimerState { EXPIRED, RESET };
enum class TouchState { INACTIVE, ACTIVE };
+ enum class DisplayPowerTimerState { EXPIRED, RESET };
// Creates a connection on the given EventThread and forwards the given callbacks.
- sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&);
+ sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&,
+ ISurfaceComposer::ConfigChanged);
nsecs_t calculateAverage() const;
void updateFrameSkipping(const int64_t skipCount);
@@ -213,12 +228,15 @@
void resetTouchTimerCallback();
// Function that is called when the touch timer expires.
void expiredTouchTimerCallback();
+ // Function that is called when the display power timer resets.
+ void resetDisplayPowerTimerCallback();
+ // Function that is called when the display power timer expires.
+ void expiredDisplayPowerTimerCallback();
// Sets vsync period.
void setVsyncPeriod(const nsecs_t period);
- // Idle timer feature's function to change the refresh rate.
- void timerChangeRefreshRate(IdleTimerState idleTimerState);
- // Touch timer feature's function to change the refresh rate.
- void touchChangeRefreshRate(TouchState touchState);
+ // handles various timer features to change the refresh rate.
+ template <class T>
+ void handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
// Calculate the new refresh rate type
RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
// Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters.
@@ -274,7 +292,12 @@
int64_t mSetTouchTimerMs = 0;
std::unique_ptr<scheduler::IdleTimer> mTouchTimer;
+ // Timer used to monitor display power mode.
+ int64_t mSetDisplayPowerTimerMs = 0;
+ std::unique_ptr<scheduler::IdleTimer> mDisplayPowerTimer;
+
std::mutex mCallbackLock;
+ GetCurrentRefreshRateTypeCallback mGetCurrentRefreshRateTypeCallback GUARDED_BY(mCallbackLock);
ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock);
@@ -285,14 +308,17 @@
ContentFeatureState::CONTENT_DETECTION_OFF;
IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
TouchState mCurrentTouchState GUARDED_BY(mFeatureStateLock) = TouchState::INACTIVE;
+ DisplayPowerTimerState mDisplayPowerTimerState GUARDED_BY(mFeatureStateLock) =
+ DisplayPowerTimerState::EXPIRED;
uint32_t mContentRefreshRate GUARDED_BY(mFeatureStateLock);
RefreshRateType mRefreshRateType GUARDED_BY(mFeatureStateLock);
bool mIsHDRContent GUARDED_BY(mFeatureStateLock) = false;
+ bool mIsDisplayPowerStateNormal GUARDED_BY(mFeatureStateLock) = true;
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
// Global config to force HDR content to work on DEFAULT refreshRate
- static constexpr bool mForceHDRContentToDefaultRefreshRate = true;
+ static constexpr bool mForceHDRContentToDefaultRefreshRate = false;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index 3bf3922..ced1899 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -36,13 +36,16 @@
static constexpr int SCREEN_OFF_CONFIG_ID = -1;
static constexpr uint32_t HWC2_SCREEN_OFF_CONFIG_ID = 0xffffffff;
-// This number is used when we try to determine how long does a given layer stay relevant.
-// Currently it is set to 100ms, because that would indicate 10Hz rendering.
-static constexpr std::chrono::nanoseconds TIME_EPSILON_NS = 100ms;
-
// This number is used when we try to determine how long do we keep layer information around
-// before we remove it. Currently it is set to 100ms.
-static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 100ms;
+// before we remove it. It is also used to determine how long the layer stays relevant.
+// This time period captures infrequent updates when playing YouTube video with static image,
+// or waiting idle in messaging app, when cursor is blinking.
+static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 1200ms;
+
+// Layer is considered low activity if the buffers come more than LOW_ACTIVITY_EPSILON_NS
+// apart. This is helping SF to vote for lower refresh rates when there is not activity
+// in screen.
+static constexpr std::chrono::nanoseconds LOW_ACTIVITY_EPSILON_NS = 250ms;
// Calculates the statistical mean (average) in the data structure (array, vector). The
// function does not modify the contents of the array.
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
new file mode 100644
index 0000000..7a3bf8e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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 "VSyncModulator.h"
+
+#include <cutils/properties.h>
+#include <utils/Trace.h>
+
+#include <cinttypes>
+#include <mutex>
+
+namespace android {
+
+using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+VSyncModulator::VSyncModulator() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.vsync_trace_detailed_info", value, "0");
+ mTraceDetailedInfo = atoi(value);
+ // Populate the offset map with some default offsets.
+ const Offsets defaultOffsets = {RefreshRateType::DEFAULT, 0, 0};
+ setPhaseOffsets(defaultOffsets, defaultOffsets, defaultOffsets, 0);
+}
+
+void VSyncModulator::setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late,
+ nsecs_t thresholdForNextVsync) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mOffsetMap.insert_or_assign(OffsetType::Early, early);
+ mOffsetMap.insert_or_assign(OffsetType::EarlyGl, earlyGl);
+ mOffsetMap.insert_or_assign(OffsetType::Late, late);
+ mThresholdForNextVsync = thresholdForNextVsync;
+ updateOffsetsLocked();
+}
+
+void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
+ if (transactionStart == Scheduler::TransactionStart::EARLY) {
+ mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
+ }
+
+ // An early transaction stays an early transaction.
+ if (transactionStart == mTransactionStart ||
+ mTransactionStart == Scheduler::TransactionStart::EARLY) {
+ return;
+ }
+ mTransactionStart = transactionStart;
+ updateOffsets();
+}
+
+void VSyncModulator::onTransactionHandled() {
+ if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
+ mTransactionStart = Scheduler::TransactionStart::NORMAL;
+ updateOffsets();
+}
+
+void VSyncModulator::onRefreshRateChangeInitiated() {
+ if (mRefreshRateChangePending) {
+ return;
+ }
+ mRefreshRateChangePending = true;
+ updateOffsets();
+}
+
+void VSyncModulator::onRefreshRateChangeCompleted() {
+ if (!mRefreshRateChangePending) {
+ return;
+ }
+ mRefreshRateChangePending = false;
+ updateOffsets();
+}
+
+void VSyncModulator::onRefreshed(bool usedRenderEngine) {
+ bool updateOffsetsNeeded = false;
+ if (mRemainingEarlyFrameCount > 0) {
+ mRemainingEarlyFrameCount--;
+ updateOffsetsNeeded = true;
+ }
+ if (usedRenderEngine) {
+ mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION;
+ updateOffsetsNeeded = true;
+ } else if (mRemainingRenderEngineUsageCount > 0) {
+ mRemainingRenderEngineUsageCount--;
+ updateOffsetsNeeded = true;
+ }
+ if (updateOffsetsNeeded) {
+ updateOffsets();
+ }
+}
+
+VSyncModulator::Offsets VSyncModulator::getOffsets() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mOffsets;
+}
+
+VSyncModulator::Offsets VSyncModulator::getNextOffsets() {
+ return mOffsetMap.at(getNextOffsetType());
+}
+
+VSyncModulator::OffsetType VSyncModulator::getNextOffsetType() {
+ // Early offsets are used if we're in the middle of a refresh rate
+ // change, or if we recently begin a transaction.
+ if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 ||
+ mRefreshRateChangePending) {
+ return OffsetType::Early;
+ } else if (mRemainingRenderEngineUsageCount > 0) {
+ return OffsetType::EarlyGl;
+ } else {
+ return OffsetType::Late;
+ }
+}
+
+void VSyncModulator::updateOffsets() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ updateOffsetsLocked();
+}
+
+void VSyncModulator::updateOffsetsLocked() {
+ const Offsets desired = getNextOffsets();
+
+ if (mSfConnectionHandle != nullptr) {
+ mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf);
+ }
+
+ if (mAppConnectionHandle != nullptr) {
+ mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
+ }
+
+ flushOffsets();
+}
+
+void VSyncModulator::flushOffsets() {
+ OffsetType type = getNextOffsetType();
+ mOffsets = mOffsetMap.at(type);
+ if (!mTraceDetailedInfo) {
+ return;
+ }
+ ATRACE_INT("Vsync-EarlyOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Early);
+ ATRACE_INT("Vsync-EarlyGLOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::EarlyGl);
+ ATRACE_INT("Vsync-LateOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Late);
+ ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Early);
+ ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::EarlyGl);
+ ATRACE_INT("Vsync-HighFpsLateOffsetsOn",
+ mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Late);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 81a7864..ddbd221 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -16,8 +16,6 @@
#pragma once
-#include <utils/Errors.h>
-
#include <cinttypes>
#include <mutex>
@@ -35,12 +33,28 @@
// sending new transactions.
const int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
+ // Number of frames we'll keep the early gl phase offsets once they are activated.
+ // This acts as a low-pass filter to avoid scenarios where we rapidly
+ // switch in and out of gl composition.
+ const int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
+
public:
+ VSyncModulator();
+
+ // Wrapper for a collection of surfaceflinger/app offsets for a particular
+ // configuration .
struct Offsets {
+ scheduler::RefreshRateConfigs::RefreshRateType fpsMode;
nsecs_t sf;
nsecs_t app;
};
+ enum class OffsetType {
+ Early,
+ EarlyGl,
+ Late,
+ };
+
// Sets the phase offsets
//
// sfEarly: The phase offset when waking up SF early, which happens when marking a transaction
@@ -51,31 +65,10 @@
// appEarly: Like sfEarly, but for the app-vsync
// appEarlyGl: Like sfEarlyGl, but for the app-vsync.
// appLate: The regular app vsync phase offset.
- void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late) {
- mEarlyOffsets = early;
- mEarlyGlOffsets = earlyGl;
- mLateOffsets = late;
+ void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late,
+ nsecs_t thresholdForNextVsync) EXCLUDES(mMutex);
- if (mSfConnectionHandle && late.sf != mOffsets.load().sf) {
- mScheduler->setPhaseOffset(mSfConnectionHandle, late.sf);
- }
-
- if (mAppConnectionHandle && late.app != mOffsets.load().app) {
- mScheduler->setPhaseOffset(mAppConnectionHandle, late.app);
- }
-
- mOffsets = late;
- }
-
- Offsets getEarlyOffsets() const { return mEarlyOffsets; }
-
- Offsets getEarlyGlOffsets() const { return mEarlyGlOffsets; }
-
- void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) {
- mSfEventThread = sfEventThread;
- mAppEventThread = appEventThread;
- }
-
+ // Sets the scheduler and vsync connection handlers.
void setSchedulerAndHandles(Scheduler* scheduler,
Scheduler::ConnectionHandle* appConnectionHandle,
Scheduler::ConnectionHandle* sfConnectionHandle) {
@@ -84,120 +77,57 @@
mSfConnectionHandle = sfConnectionHandle;
}
- void setTransactionStart(Scheduler::TransactionStart transactionStart) {
- if (transactionStart == Scheduler::TransactionStart::EARLY) {
- mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
- }
+ // Signals that a transaction has started, and changes offsets accordingly.
+ void setTransactionStart(Scheduler::TransactionStart transactionStart);
- // An early transaction stays an early transaction.
- if (transactionStart == mTransactionStart ||
- mTransactionStart == Scheduler::TransactionStart::EARLY) {
- return;
- }
- mTransactionStart = transactionStart;
- updateOffsets();
- }
-
- void onTransactionHandled() {
- if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
- mTransactionStart = Scheduler::TransactionStart::NORMAL;
- updateOffsets();
- }
+ // Signals that a transaction has been completed, so that we can finish
+ // special handling for a transaction.
+ void onTransactionHandled();
// Called when we send a refresh rate change to hardware composer, so that
// we can move into early offsets.
- void onRefreshRateChangeInitiated() {
- if (mRefreshRateChangePending) {
- return;
- }
- mRefreshRateChangePending = true;
- updateOffsets();
- }
+ void onRefreshRateChangeInitiated();
// Called when we detect from vsync signals that the refresh rate changed.
// This way we can move out of early offsets if no longer necessary.
- void onRefreshRateChangeDetected() {
- if (!mRefreshRateChangePending) {
- return;
- }
- mRefreshRateChangePending = false;
- updateOffsets();
- }
+ void onRefreshRateChangeCompleted();
- void onRefreshed(bool usedRenderEngine) {
- bool updateOffsetsNeeded = false;
- if (mRemainingEarlyFrameCount > 0) {
- mRemainingEarlyFrameCount--;
- updateOffsetsNeeded = true;
- }
- if (usedRenderEngine != mLastFrameUsedRenderEngine) {
- mLastFrameUsedRenderEngine = usedRenderEngine;
- updateOffsetsNeeded = true;
- }
- if (updateOffsetsNeeded) {
- updateOffsets();
- }
- }
+ // Called when the display is presenting a new frame. usedRenderEngine
+ // should be set to true if RenderEngine was involved with composing the new
+ // frame.
+ void onRefreshed(bool usedRenderEngine);
- Offsets getOffsets() {
- // Early offsets are used if we're in the middle of a refresh rate
- // change, or if we recently begin a transaction.
- if (mTransactionStart == Scheduler::TransactionStart::EARLY ||
- mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
- return mEarlyOffsets;
- } else if (mLastFrameUsedRenderEngine) {
- return mEarlyGlOffsets;
- } else {
- return mLateOffsets;
- }
- }
+ // Returns the offsets that we are currently using
+ Offsets getOffsets() EXCLUDES(mMutex);
private:
- void updateOffsets() {
- const Offsets desired = getOffsets();
- const Offsets current = mOffsets;
+ // Returns the next offsets that we should be using
+ Offsets getNextOffsets() REQUIRES(mMutex);
+ // Returns the next offset type that we should use.
+ OffsetType getNextOffsetType();
+ // Updates offsets and persists them into the scheduler framework.
+ void updateOffsets() EXCLUDES(mMutex);
+ void updateOffsetsLocked() REQUIRES(mMutex);
+ // Updates the internal offsets and offset type.
+ void flushOffsets() REQUIRES(mMutex);
- bool changed = false;
- if (desired.sf != current.sf) {
- if (mSfConnectionHandle != nullptr) {
- mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf);
- } else {
- mSfEventThread->setPhaseOffset(desired.sf);
- }
- changed = true;
- }
- if (desired.app != current.app) {
- if (mAppConnectionHandle != nullptr) {
- mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
- } else {
- mAppEventThread->setPhaseOffset(desired.app);
- }
- changed = true;
- }
-
- if (changed) {
- mOffsets = desired;
- }
- }
-
- Offsets mLateOffsets;
- Offsets mEarlyOffsets;
- Offsets mEarlyGlOffsets;
-
- EventThread* mSfEventThread = nullptr;
- EventThread* mAppEventThread = nullptr;
+ mutable std::mutex mMutex;
+ std::unordered_map<OffsetType, Offsets> mOffsetMap GUARDED_BY(mMutex);
+ nsecs_t mThresholdForNextVsync;
Scheduler* mScheduler = nullptr;
Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr;
Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr;
- std::atomic<Offsets> mOffsets;
+ Offsets mOffsets GUARDED_BY(mMutex) = {Scheduler::RefreshRateType::DEFAULT, 0, 0};
std::atomic<Scheduler::TransactionStart> mTransactionStart =
Scheduler::TransactionStart::NORMAL;
- std::atomic<bool> mLastFrameUsedRenderEngine = false;
std::atomic<bool> mRefreshRateChangePending = false;
std::atomic<int> mRemainingEarlyFrameCount = 0;
+ std::atomic<int> mRemainingRenderEngineUsageCount = 0;
+
+ bool mTraceDetailedInfo = false;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9c8251f..066b60a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -311,6 +311,9 @@
wideColorGamutCompositionPixelFormat =
static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888));
+ mColorSpaceAgnosticDataspace =
+ static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN));
+
useContextPriority = use_context_priority(true);
auto tmpPrimaryDisplayOrientation = primary_display_orientation(
@@ -377,7 +380,8 @@
mLumaSampling = atoi(value);
const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
- mVsyncModulator.setPhaseOffsets(early, gl, late);
+ mVsyncModulator.setPhaseOffsets(early, gl, late,
+ mPhaseOffsets->getOffsetThresholdForNextVsync());
// We should be reading 'persist.sys.sf.color_saturation' here
// but since /data may be encrypted, we need to wait until after vold
@@ -616,13 +620,16 @@
mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
mAppConnectionHandle =
- mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
+ mScheduler->createConnection("app", mVsyncModulator.getOffsets().app,
+ mPhaseOffsets->getOffsetThresholdForNextVsync(),
resyncCallback,
impl::EventThread::InterceptVSyncsCallback());
- mSfConnectionHandle = mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
- resyncCallback, [this](nsecs_t timestamp) {
- mInterceptor->saveVSyncEvent(timestamp);
- });
+ mSfConnectionHandle =
+ mScheduler->createConnection("sf", mVsyncModulator.getOffsets().sf,
+ mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ resyncCallback, [this](nsecs_t timestamp) {
+ mInterceptor->saveVSyncEvent(timestamp);
+ });
mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
@@ -706,6 +713,24 @@
Mutex::Autolock lock(mStateLock);
setRefreshRateTo(type, event);
});
+ mScheduler->setGetCurrentRefreshRateTypeCallback([this] {
+ Mutex::Autolock lock(mStateLock);
+ const auto display = getDefaultDisplayDeviceLocked();
+ if (!display) {
+ // If we don't have a default display the fallback to the default
+ // refresh rate type
+ return RefreshRateType::DEFAULT;
+ }
+
+ const int configId = display->getActiveConfig();
+ for (const auto& [type, refresh] : mRefreshRateConfigs.getRefreshRates()) {
+ if (refresh && refresh->configId == configId) {
+ return type;
+ }
+ }
+ // This should never happen, but just gracefully fallback to default.
+ return RefreshRateType::DEFAULT;
+ });
mScheduler->setGetVsyncPeriodCallback([this] {
Mutex::Autolock lock(mStateLock);
return getVsyncPeriod();
@@ -935,10 +960,13 @@
// Start receiving vsync samples now, so that we can detect a period
// switch.
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ // As we called to set period, we will call to onRefreshRateChangeCompleted once
+ // DispSync model is locked.
+ mVsyncModulator.onRefreshRateChangeInitiated();
mPhaseOffsets->setRefreshRateType(info.type);
const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
- mVsyncModulator.onRefreshRateChangeInitiated();
- mVsyncModulator.setPhaseOffsets(early, gl, late);
+ mVsyncModulator.setPhaseOffsets(early, gl, late,
+ mPhaseOffsets->getOffsetThresholdForNextVsync());
}
mDesiredActiveConfigChanged = true;
ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
@@ -970,10 +998,10 @@
display->setActiveConfig(mUpcomingActiveConfig.configId);
- mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
- mVsyncModulator.setPhaseOffsets(early, gl, late);
+ mVsyncModulator.setPhaseOffsets(early, gl, late,
+ mPhaseOffsets->getOffsetThresholdForNextVsync());
ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
@@ -982,6 +1010,19 @@
}
}
+void SurfaceFlinger::desiredActiveConfigChangeDone() {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
+ mDesiredActiveConfigChanged = false;
+ ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+
+ mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
+ const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+ mVsyncModulator.setPhaseOffsets(early, gl, late,
+ mPhaseOffsets->getOffsetThresholdForNextVsync());
+}
+
bool SurfaceFlinger::performSetActiveConfig() {
ATRACE_CALL();
if (mCheckPendingFence) {
@@ -1011,10 +1052,7 @@
if (!display || display->getActiveConfig() == desiredActiveConfig.configId) {
// display is not valid or we are already in the requested mode
// on both cases there is nothing left to do
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
- mDesiredActiveConfigChanged = false;
- ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+ desiredActiveConfigChangeDone();
return false;
}
@@ -1022,13 +1060,10 @@
// allowed configs might have change by the time we process the refresh.
// Make sure the desired config is still allowed
if (!isDisplayConfigAllowed(desiredActiveConfig.configId)) {
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
- mDesiredActiveConfig.configId = display->getActiveConfig();
- mDesiredActiveConfigChanged = false;
- ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
+ desiredActiveConfigChangeDone();
return false;
}
+
mUpcomingActiveConfig = desiredActiveConfig;
const auto displayId = display->getId();
LOG_ALWAYS_FATAL_IF(!displayId);
@@ -1366,7 +1401,7 @@
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
- ISurfaceComposer::VsyncSource vsyncSource) {
+ ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
auto resyncCallback = mScheduler->makeResyncCallback([this] {
Mutex::Autolock lock(mStateLock);
return getVsyncPeriod();
@@ -1375,7 +1410,8 @@
const auto& handle =
vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
- return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback));
+ return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback),
+ configChanged);
}
// ----------------------------------------------------------------------------
@@ -1448,10 +1484,10 @@
return;
}
- bool periodChanged = false;
- mScheduler->addResyncSample(timestamp, &periodChanged);
- if (periodChanged) {
- mVsyncModulator.onRefreshRateChangeDetected();
+ bool periodFlushed = false;
+ mScheduler->addResyncSample(timestamp, &periodFlushed);
+ if (periodFlushed) {
+ mVsyncModulator.onRefreshRateChangeCompleted();
}
}
@@ -1524,10 +1560,23 @@
void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
ATRACE_CALL();
- Mutex::Autolock lock(mStateLock);
+
+ // Enable / Disable HWVsync from the main thread to avoid race conditions with
+ // display power state.
+ postMessageAsync(new LambdaMessage(
+ [=]() NO_THREAD_SAFETY_ANALYSIS { setPrimaryVsyncEnabledInternal(enabled); }));
+}
+
+void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) {
+ ATRACE_CALL();
+
+ mHWCVsyncPendingState = enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable;
+
if (const auto displayId = getInternalDisplayIdLocked()) {
- getHwComposer().setVsyncEnabled(*displayId,
- enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
+ sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
+ if (display && display->isPoweredOn()) {
+ setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+ }
}
}
@@ -1637,6 +1686,18 @@
return fence != Fence::NO_FENCE && (fence->getStatus() == Fence::Status::Unsignaled);
}
+nsecs_t SurfaceFlinger::getExpectedPresentTime() NO_THREAD_SAFETY_ANALYSIS {
+ DisplayStatInfo stats;
+ mScheduler->getDisplayStatInfo(&stats);
+ const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime();
+ // Inflate the expected present time if we're targetting the next vsync.
+ const nsecs_t correctedTime =
+ mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
+ ? presentTime
+ : presentTime + stats.vsyncPeriod;
+ return correctedTime;
+}
+
void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
ATRACE_CALL();
switch (what) {
@@ -1840,7 +1901,14 @@
RenderIntent renderIntent;
pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent);
display->setColorMode(colorMode, targetDataspace, renderIntent);
+
+ if (isHdrColorMode(colorMode)) {
+ targetDataspace = Dataspace::UNKNOWN;
+ } else if (mColorSpaceAgnosticDataspace != Dataspace::UNKNOWN) {
+ targetDataspace = mColorSpaceAgnosticDataspace;
+ }
}
+
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
if (layer->isHdrY410()) {
layer->forceClientComposition(displayDevice);
@@ -1867,9 +1935,7 @@
const auto& displayState = display->getState();
layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport,
- displayDevice->getSupportedPerFrameMetadata(),
- isHdrColorMode(displayState.colorMode) ? Dataspace::UNKNOWN
- : targetDataspace);
+ displayDevice->getSupportedPerFrameMetadata(), targetDataspace);
}
}
@@ -3245,8 +3311,7 @@
mDrawingState.traverseInZOrder([&](Layer* layer) {
if (layer->hasReadyFrame()) {
frameQueued = true;
- nsecs_t expectedPresentTime;
- expectedPresentTime = mScheduler->expectedPresentTime();
+ const nsecs_t expectedPresentTime = getExpectedPresentTime();
if (layer->shouldPresentNow(expectedPresentTime)) {
mLayersWithQueuedFrames.push_back(layer);
} else {
@@ -3643,7 +3708,7 @@
bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
const Vector<ComposerState>& states) {
- nsecs_t expectedPresentTime = mScheduler->expectedPresentTime();
+ nsecs_t expectedPresentTime = getExpectedPresentTime();
// Do not present if the desiredPresentTime has not passed unless it is more than one second
// in the future. We ignore timestamps more than 1 second in the future for stability reasons.
if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
@@ -4123,6 +4188,9 @@
sp<GraphicBuffer> buffer;
if (bufferChanged && cacheIdChanged) {
ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
+ ClientCache::getInstance().registerErasedRecipient(s.cachedBuffer,
+ wp<ClientCache::ErasedRecipient>(layer));
+ getRenderEngine().cacheExternalTextureBuffer(s.buffer);
buffer = s.buffer;
} else if (cacheIdChanged) {
buffer = ClientCache::getInstance().get(s.cachedBuffer);
@@ -4406,6 +4474,13 @@
new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); }));
}
+void SurfaceFlinger::setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled) {
+ if (mHWCVsyncState != enabled) {
+ getHwComposer().setVsyncEnabled(displayId, enabled);
+ mHWCVsyncState = enabled;
+ }
+}
+
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mode) {
if (display->isVirtual()) {
ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
@@ -4432,6 +4507,7 @@
// Turn on the display
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
+ setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
}
@@ -4457,6 +4533,9 @@
mScheduler->onScreenReleased(mAppConnectionHandle);
}
+ // Make sure HWVsync is disabled before turning off the display
+ setVsyncEnabledInHWC(*displayId, HWC2::Vsync::Disable);
+
getHwComposer().setPowerMode(*displayId, mode);
mVisibleRegionsDirty = true;
// from this point on, SF will stop drawing on this display
@@ -4483,6 +4562,7 @@
if (display->isPrimary()) {
mTimeStats->setPowerMode(mode);
mRefreshRateStats.setPowerMode(mode);
+ mScheduler->setDisplayPowerState(mode == HWC_POWER_MODE_NORMAL);
}
ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
@@ -4647,6 +4727,16 @@
StringAppendF(&result, "Scheduler enabled.");
StringAppendF(&result, "+ Smart 90 for video detection: %s\n\n",
mUseSmart90ForVideo ? "on" : "off");
+ StringAppendF(&result, "Allowed Display Configs: ");
+ for (int32_t configId : mAllowedDisplayConfigs) {
+ for (auto refresh : mRefreshRateConfigs.getRefreshRates()) {
+ if (refresh.second && refresh.second->configId == configId) {
+ StringAppendF(&result, "%dHz, ", refresh.second->fps);
+ }
+ }
+ }
+ StringAppendF(&result, "(config override by backdoor: %s)\n\n",
+ mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
mScheduler->dump(mAppConnectionHandle, result);
}
@@ -5345,7 +5435,12 @@
return NO_ERROR;
}
case 1023: { // Set native mode
+ int32_t colorMode;
+
mDisplayColorSetting = static_cast<DisplayColorSetting>(data.readInt32());
+ if (data.readInt32(&colorMode) == NO_ERROR) {
+ mForceColorMode = static_cast<ColorMode>(colorMode);
+ }
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c5c4103..9de2aad 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -293,11 +293,19 @@
// TODO: this should be made accessible only to EventThread
void setPrimaryVsyncEnabled(bool enabled);
+ // main thread function to enable/disable h/w composer event
+ void setPrimaryVsyncEnabledInternal(bool enabled);
+
// called on the main thread by MessageQueue when an internal message
// is received
// TODO: this should be made accessible only to MessageQueue
void onMessageReceived(int32_t what);
+ // Returns the expected present time for this frame.
+ // When we are in negative offsets, we perform a correction so that the
+ // predicted vsync for the *next* frame is used instead.
+ nsecs_t getExpectedPresentTime();
+
// for debugging only
// TODO: this should be made accessible only to HWComposer
const Vector<sp<Layer>>& getLayerSortedByZForHwcDisplay(DisplayId displayId);
@@ -399,7 +407,9 @@
const sp<IGraphicBufferProducer>& bufferProducer) const override;
status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override;
sp<IDisplayEventConnection> createDisplayEventConnection(
- ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp) override;
+ ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
+ ISurfaceComposer::ConfigChanged configChanged =
+ ISurfaceComposer::eConfigChangedSuppress) override;
status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace,
const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
@@ -508,13 +518,15 @@
// Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
void setDesiredActiveConfig(const ActiveConfigInfo& info) REQUIRES(mStateLock);
// Once HWC has returned the present fence, this sets the active config and a new refresh
- // rate in SF. It also triggers HWC vsync.
+ // rate in SF.
void setActiveConfigInternal() REQUIRES(mStateLock);
// Active config is updated on INVALIDATE call in a state machine-like manner. When the
// desired config was set, HWC needs to update the panel on the next refresh, and when
// we receive the fence back, we know that the process was complete. It returns whether
// we need to wait for the next invalidate
bool performSetActiveConfig() REQUIRES(mStateLock);
+ // Called when active config is no longer is progress
+ void desiredActiveConfigChangeDone() REQUIRES(mStateLock);
// called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
@@ -833,6 +845,7 @@
}
bool previousFrameMissed();
+ void setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled);
/*
* Debugging & dumpsys
@@ -1105,6 +1118,7 @@
ui::Dataspace mDefaultCompositionDataspace;
ui::Dataspace mWideColorGamutCompositionDataspace;
+ ui::Dataspace mColorSpaceAgnosticDataspace;
SurfaceFlingerBE mBE;
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
@@ -1169,6 +1183,10 @@
// The Layer pointer is removed from the set when the destructor is called so there shouldn't
// be any issues with a raw pointer referencing an invalid object.
std::unordered_set<Layer*> mOffscreenLayers;
+
+ // Flags to capture the state of Vsync in HWC
+ HWC2::Vsync mHWCVsyncState = HWC2::Vsync::Disable;
+ HWC2::Vsync mHWCVsyncPendingState = HWC2::Vsync::Disable;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 2b33ba1..768074a 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -218,6 +218,14 @@
return static_cast<int32_t>(defaultValue);
}
+int64_t color_space_agnostic_dataspace(Dataspace defaultValue) {
+ auto temp = SurfaceFlingerProperties::color_space_agnostic_dataspace();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return static_cast<int64_t>(defaultValue);
+}
+
int32_t set_idle_timer_ms(int32_t defaultValue) {
auto temp = SurfaceFlingerProperties::set_idle_timer_ms();
if (temp.has_value()) {
@@ -234,6 +242,14 @@
return defaultValue;
}
+int32_t set_display_power_timer_ms(int32_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::set_display_power_timer_ms();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
bool use_smart_90_for_video(bool defaultValue) {
auto temp = SurfaceFlingerProperties::use_smart_90_for_video();
if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 1964ccd..5f88322 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -70,10 +70,15 @@
int32_t wcg_composition_pixel_format(
android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
+int64_t color_space_agnostic_dataspace(
+ android::hardware::graphics::common::V1_2::Dataspace defaultValue);
+
int32_t set_idle_timer_ms(int32_t defaultValue);
int32_t set_touch_timer_ms(int32_t defaultValue);
+int32_t set_display_power_timer_ms(int32_t defaultValue);
+
bool use_smart_90_for_video(bool defaultValue);
bool enable_protected_contents(bool defaultValue);
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index decabd5..74baf37 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -251,6 +251,20 @@
prop_name: "ro.surface_flinger.wcg_composition_pixel_format"
}
+# colorSpaceAgnosticDataspace specifies the data space that
+# SurfaceFlinger expects for surfaces which are color space agnostic.
+# The variable works only when useColorManagement is specified. If
+# unspecified, the data space follows what SurfaceFlinger expects for
+# surfaces when useColorManagement is specified.
+
+prop {
+ api_name: "color_space_agnostic_dataspace"
+ type: Long
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.color_space_agnostic_dataspace"
+}
+
# Return the native panel primary data. The data includes red, green,
# blue and white. The primary format is CIE 1931 XYZ color space.
# If unspecified, the primaries is sRGB gamut by default.
@@ -309,6 +323,18 @@
prop_name: "ro.surface_flinger.set_touch_timer_ms"
}
+# setDisplayPowerTimerMs indicates what is considered a timeout in milliseconds for Scheduler.
+# This value is used by the Scheduler to trigger display power inactivity callbacks that will
+# keep the display in peak refresh rate as long as display power is not in normal mode.
+# Setting this property to 0 means there is no timer.
+prop {
+ api_name: "set_display_power_timer_ms"
+ type: Integer
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.set_display_power_timer_ms"
+}
+
# useSmart90ForVideo indicates whether Scheduler should detect content FPS, and try to adjust the
# screen refresh rate based on that.
prop {
diff --git a/services/surfaceflinger/sysprop/api/system-current.txt b/services/surfaceflinger/sysprop/api/system-current.txt
index 6ae3ac1..79854b3 100644
--- a/services/surfaceflinger/sysprop/api/system-current.txt
+++ b/services/surfaceflinger/sysprop/api/system-current.txt
@@ -2,6 +2,7 @@
package android.sysprop {
public final class SurfaceFlingerProperties {
+ method public static java.util.Optional<java.lang.Long> color_space_agnostic_dataspace();
method public static java.util.Optional<java.lang.Long> default_composition_dataspace();
method public static java.util.Optional<java.lang.Integer> default_composition_pixel_format();
method public static java.util.List<java.lang.Double> display_primary_blue();
@@ -17,6 +18,7 @@
method public static java.util.Optional<java.lang.Long> present_time_offset_from_vsync_ns();
method public static java.util.Optional<android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values> primary_display_orientation();
method public static java.util.Optional<java.lang.Boolean> running_without_sync_framework();
+ method public static java.util.Optional<java.lang.Integer> set_display_power_timer_ms();
method public static java.util.Optional<java.lang.Integer> set_idle_timer_ms();
method public static java.util.Optional<java.lang.Integer> set_touch_timer_ms();
method public static java.util.Optional<java.lang.Boolean> start_graphics_allocator_service();
diff --git a/services/surfaceflinger/test.png b/services/surfaceflinger/test.png
new file mode 100644
index 0000000..773dcc3
--- /dev/null
+++ b/services/surfaceflinger/test.png
Binary files differ
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 2e705da..0aa8cf5 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -51,6 +51,7 @@
AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
+ static constexpr std::chrono::nanoseconds mOffsetThresholdForNextVsync = 16ms;
static constexpr int mIterations = 100;
};
@@ -78,7 +79,8 @@
void DispSyncSourceTest::createDispSyncSource() {
createDispSync();
- mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
+ mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(),
+ mOffsetThresholdForNextVsync.count(), true,
"DispSyncSourceTest");
mDispSyncSource->setCallback(this);
}
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index ea908a9..dbd9b84 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -55,8 +55,9 @@
class MockEventThreadConnection : public EventThreadConnection {
public:
MockEventThreadConnection(android::impl::EventThread* eventThread,
- ResyncCallback&& resyncCallback)
- : EventThreadConnection(eventThread, std::move(resyncCallback)) {}
+ ResyncCallback&& resyncCallback,
+ ISurfaceComposer::ConfigChanged configChanged)
+ : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -67,7 +68,8 @@
~EventThreadTest() override;
void createThread();
- sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder);
+ sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
+ ISurfaceComposer::ConfigChanged configChanged);
void expectVSyncSetEnabledCallReceived(bool expectedState);
void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset);
@@ -110,7 +112,8 @@
.WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
createThread();
- mConnection = createConnection(mConnectionEventCallRecorder);
+ mConnection = createConnection(mConnectionEventCallRecorder,
+ ISurfaceComposer::eConfigChangedDispatch);
// A display must be connected for VSYNC events to be delivered.
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
@@ -138,9 +141,10 @@
}
sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
- ConnectionEventRecorder& recorder) {
+ ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged) {
sp<MockEventThreadConnection> connection =
- new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable());
+ new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(),
+ configChanged);
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -267,7 +271,9 @@
TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
// Create a first connection, register it, and request a vsync rate of zero.
ConnectionEventRecorder firstConnectionEventRecorder{0};
- sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder);
+ sp<MockEventThreadConnection> firstConnection =
+ createConnection(firstConnectionEventRecorder,
+ ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(0, firstConnection);
// By itself, this should not enable vsync events
@@ -277,7 +283,8 @@
// However if there is another connection which wants events at a nonzero rate.....
ConnectionEventRecorder secondConnectionEventRecorder{0};
sp<MockEventThreadConnection> secondConnection =
- createConnection(secondConnectionEventRecorder);
+ createConnection(secondConnectionEventRecorder,
+ ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, secondConnection);
// EventThread should enable vsync callbacks.
@@ -363,7 +370,9 @@
TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
- sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
+ sp<MockEventThreadConnection> errorConnection =
+ createConnection(errorConnectionEventRecorder,
+ ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, errorConnection);
// EventThread should enable vsync callbacks.
@@ -387,7 +396,9 @@
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
- sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
+ sp<MockEventThreadConnection> errorConnection =
+ createConnection(errorConnectionEventRecorder,
+ ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, errorConnection);
// EventThread should enable vsync callbacks.
@@ -449,5 +460,18 @@
expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7);
}
+TEST_F(EventThreadTest, suppressConfigChanged) {
+ ConnectionEventRecorder suppressConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> suppressConnection =
+ createConnection(suppressConnectionEventRecorder,
+ ISurfaceComposer::eConfigChangedSuppress);
+
+ mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 9);
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9);
+
+ auto args = suppressConnectionEventRecorder.waitForCall();
+ ASSERT_FALSE(args.has_value());
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index 96121bb..1d75011 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -23,6 +23,8 @@
namespace android {
namespace scheduler {
+using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+
class FakePhaseOffsets : public android::scheduler::PhaseOffsets {
nsecs_t FAKE_PHASE_OFFSET_NS = 0;
@@ -34,20 +36,20 @@
nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; }
PhaseOffsets::Offsets getOffsetsForRefreshRate(
- RefreshRateConfigs::RefreshRateType /*refreshRateType*/) const override {
+ RefreshRateType /*refreshRateType*/) const override {
return getCurrentOffsets();
}
// Returns early, early GL, and late offsets for Apps and SF.
PhaseOffsets::Offsets getCurrentOffsets() const override {
- return Offsets{{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
+ return Offsets{{RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+ {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+ {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
}
// This function should be called when the device is switching between different
// refresh rates, to properly update the offsets.
- void setRefreshRateType(RefreshRateConfigs::RefreshRateType /*refreshRateType*/) override {}
+ void setRefreshRateType(RefreshRateType /*refreshRateType*/) override {}
nsecs_t getOffsetThresholdForNextVsync() const override { return FAKE_PHASE_OFFSET_NS; }
@@ -56,4 +58,4 @@
};
} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 2b1dfa8..7ec9066 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -25,6 +25,7 @@
protected:
std::unique_ptr<LayerHistory> mLayerHistory;
+ static constexpr float MIN_REFRESH_RATE = 30.f;
static constexpr float MAX_REFRESH_RATE = 90.f;
};
@@ -36,7 +37,7 @@
namespace {
TEST_F(LayerHistoryTest, oneLayer) {
std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(testLayer, true);
mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
@@ -60,7 +61,7 @@
TEST_F(LayerHistoryTest, oneHDRLayer) {
std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestHDRLayer", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("TestHDRLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(testLayer, true);
mLayerHistory->insert(testLayer, 0, true /*isHDR*/);
@@ -74,7 +75,7 @@
TEST_F(LayerHistoryTest, explicitTimestamp) {
std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
- mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(test30FpsLayer, true);
nsecs_t startTime = systemTime();
@@ -87,13 +88,13 @@
TEST_F(LayerHistoryTest, multipleLayers) {
std::unique_ptr<LayerHistory::LayerHandle> testLayer =
- mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(testLayer, true);
std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
- mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(test30FpsLayer, true);
std::unique_ptr<LayerHistory::LayerHandle> testLayer2 =
- mLayerHistory->createLayer("TestLayer2", MAX_REFRESH_RATE);
+ mLayerHistory->createLayer("TestLayer2", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(testLayer2, true);
nsecs_t startTime = systemTime();
@@ -119,8 +120,8 @@
mLayerHistory->insert(testLayer2, 0, false /*isHDR*/);
}
EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- // After 100 ms frames become obsolete.
- std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ // After 1200 ms frames become obsolete.
+ std::this_thread::sleep_for(std::chrono::milliseconds(1500));
// Insert the 31st frame.
mLayerHistory->insert(test30FpsLayer, startTime + (30 * 33333333), false /*isHDR*/);
EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 1f8b111..740115e 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -25,7 +25,8 @@
class MockEventThreadConnection : public android::EventThreadConnection {
public:
explicit MockEventThreadConnection(EventThread* eventThread)
- : EventThreadConnection(eventThread, ResyncCallback()) {}
+ : EventThreadConnection(eventThread, ResyncCallback(),
+ ISurfaceComposer::eConfigChangedSuppress) {}
~MockEventThreadConnection() = default;
MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
@@ -47,7 +48,7 @@
std::unique_ptr<EventThread> makeEventThread(
const char* /* connectionName */, DispSync* /* dispSync */,
- nsecs_t /* phaseOffsetNs */,
+ nsecs_t /* phaseOffsetNs */, nsecs_t /* offsetThresholdForNextVsync */,
impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override {
return std::move(mEventThread);
}
@@ -81,10 +82,10 @@
// createConnection call to scheduler makes a createEventConnection call to EventThread. Make
// sure that call gets executed and returns an EventThread::Connection object.
- EXPECT_CALL(*mEventThread, createEventConnection(_))
+ EXPECT_CALL(*mEventThread, createEventConnection(_, _))
.WillRepeatedly(Return(mEventThreadConnection));
- mConnectionHandle = mScheduler->createConnection("appConnection", 16, ResyncCallback(),
+ mConnectionHandle = mScheduler->createConnection("appConnection", 16, 16, ResyncCallback(),
impl::EventThread::InterceptVSyncsCallback());
EXPECT_TRUE(mConnectionHandle != nullptr);
}
@@ -105,7 +106,10 @@
// exceptions, just gracefully continues.
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
- returnedValue = mScheduler->createDisplayEventConnection(nullptr, ResyncCallback()));
+ returnedValue =
+ mScheduler->createDisplayEventConnection(nullptr, ResyncCallback(),
+ ISurfaceComposer::
+ eConfigChangedSuppress));
EXPECT_TRUE(returnedValue == nullptr);
EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr);
EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr);
@@ -126,7 +130,9 @@
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
returnedValue =
- mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback()));
+ mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback(),
+ ISurfaceComposer::
+ eConfigChangedSuppress));
EXPECT_TRUE(returnedValue == nullptr);
EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr);
EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr);
@@ -155,7 +161,9 @@
sp<IDisplayEventConnection> returnedValue;
ASSERT_NO_FATAL_FAILURE(
returnedValue =
- mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback()));
+ mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback(),
+ ISurfaceComposer::
+ eConfigChangedSuppress));
EXPECT_TRUE(returnedValue != nullptr);
ASSERT_EQ(returnedValue, mEventThreadConnection);
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index c3d2b8d..cb6980e 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -17,6 +17,7 @@
#pragma once
#include <gmock/gmock.h>
+#include <gui/ISurfaceComposer.h>
#include "Scheduler/EventThread.h"
#include "Scheduler/RefreshRateConfigs.h"
@@ -34,7 +35,8 @@
// Scheduler::Connection. This allows plugging in mock::EventThread.
sp<Scheduler::ConnectionHandle> addConnection(std::unique_ptr<EventThread> eventThread) {
sp<EventThreadConnection> eventThreadConnection =
- new EventThreadConnection(eventThread.get(), ResyncCallback());
+ new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ ISurfaceComposer::eConfigChangedSuppress);
const int64_t id = sNextId++;
mConnections.emplace(id,
std::make_unique<Scheduler::Connection>(new ConnectionHandle(id),
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
index 12a349d..9ca116d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
@@ -35,6 +35,7 @@
MOCK_METHOD0(endResync, void());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(getPeriod, nsecs_t());
+ MOCK_METHOD0(getIntendedPeriod, nsecs_t());
MOCK_METHOD1(setRefreshSkipCount, void(int));
MOCK_CONST_METHOD1(computeNextRefresh, nsecs_t(int));
MOCK_METHOD1(setIgnorePresentFences, void(bool));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 5b5f8e7..ed35ebf 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -28,7 +28,8 @@
EventThread();
~EventThread() override;
- MOCK_CONST_METHOD1(createEventConnection, sp<EventThreadConnection>(ResyncCallback));
+ MOCK_CONST_METHOD2(createEventConnection,
+ sp<EventThreadConnection>(ResyncCallback, ISurfaceComposer::ConfigChanged));
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));