Merge "Improve dump: type & touchOcclusionMode"
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index cbcf6ec..7a74248 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -48,7 +48,7 @@
/**
* Creates an ASurfaceControl with either ANativeWindow or an ASurfaceControl as its parent.
- * |debug_name| is a debug name associated with this surface. It can be used to
+ * \a debug_name is a debug name associated with this surface. It can be used to
* identify this surface in the SurfaceFlinger's layer tree. It must not be
* null.
*
@@ -69,7 +69,7 @@
__INTRODUCED_IN(29);
/**
- * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer
+ * Releases the \a surface_control object. After releasing the ASurfaceControl the caller no longer
* has ownership of the AsurfaceControl. The surface and it's children may remain on display as long
* as their parent remains on display.
*
@@ -87,21 +87,21 @@
/**
* The caller takes ownership of the transaction and must release it using
- * ASurfaceControl_delete below.
+ * ASurfaceTransaction_delete() below.
*
* Available since API level 29.
*/
ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29);
/**
- * Destroys the |transaction| object.
+ * Destroys the \a transaction object.
*
* Available since API level 29.
*/
void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
/**
- * Applies the updates accumulated in |transaction|.
+ * Applies the updates accumulated in \a transaction.
*
* Note that the transaction is guaranteed to be applied atomically. The
* transactions which are applied on the same thread are also guaranteed to be
@@ -123,10 +123,10 @@
* ASurfaceTransaction_OnComplete callback can be used to be notified when a frame
* including the updates in a transaction was presented.
*
- * |context| is the optional context provided by the client that is passed into
+ * \param context Optional context provided by the client that is passed into
* the callback.
*
- * |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query
+ * \param stats Opaque handle that can be passed to ASurfaceTransactionStats functions to query
* information about the transaction. The handle is only valid during the callback.
*
* THREADING
@@ -157,14 +157,14 @@
__INTRODUCED_IN(29);
/**
- * |outASurfaceControls| returns an array of ASurfaceControl pointers that were updated during the
+ * \a outASurfaceControls returns an array of ASurfaceControl pointers that were updated during the
* transaction. Stats for the surfaces can be queried through ASurfaceTransactionStats functions.
* When the client is done using the array, it must release it by calling
* ASurfaceTransactionStats_releaseASurfaceControls.
*
* Available since API level 29.
*
- * |outASurfaceControlsSize| returns the size of the ASurfaceControls array.
+ * \a outASurfaceControlsSize returns the size of the ASurfaceControls array.
*/
void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats,
ASurfaceControl*** outASurfaceControls,
@@ -172,7 +172,7 @@
__INTRODUCED_IN(29);
/**
* Releases the array of ASurfaceControls that were returned by
- * ASurfaceTransactionStats_getASurfaceControls.
+ * ASurfaceTransactionStats_getASurfaceControls().
*
* Available since API level 29.
*/
@@ -197,8 +197,8 @@
* buffer is already released. The recipient of the callback takes ownership of the
* previousReleaseFenceFd and is responsible for closing it.
*
- * Each time a buffer is set through ASurfaceTransaction_setBuffer()/_setCachedBuffer() on a
- * transaction which is applied, the framework takes a ref on this buffer. The framework treats the
+ * Each time a buffer is set through ASurfaceTransaction_setBuffer() on a transaction
+ * which is applied, the framework takes a ref on this buffer. The framework treats the
* addition of a buffer to a particular surface as a unique ref. When a transaction updates or
* removes a buffer from a surface, or removes the surface itself from the tree, this ref is
* guaranteed to be released in the OnComplete callback for this transaction. The
@@ -226,10 +226,10 @@
ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
/**
- * Reparents the |surface_control| from its old parent to the |new_parent| surface control.
- * Any children of the* reparented |surface_control| will remain children of the |surface_control|.
+ * Reparents the \a surface_control from its old parent to the \a new_parent surface control.
+ * Any children of the reparented \a surface_control will remain children of the \a surface_control.
*
- * The |new_parent| can be null. Surface controls with a null parent do not appear on the display.
+ * The \a new_parent can be null. Surface controls with a null parent do not appear on the display.
*
* Available since API level 29.
*/
@@ -237,14 +237,16 @@
ASurfaceControl* surface_control, ASurfaceControl* new_parent)
__INTRODUCED_IN(29);
-/* Parameter for ASurfaceTransaction_setVisibility */
+/**
+ * Parameter for ASurfaceTransaction_setVisibility().
+ */
enum {
ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0,
ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1,
};
/**
- * Updates the visibility of |surface_control|. If show is set to
- * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the |surface_control| and all surfaces in its subtree will
+ * Updates the visibility of \a surface_control. If show is set to
+ * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the \a surface_control and all surfaces in its subtree will
* be hidden.
*
* Available since API level 29.
@@ -254,7 +256,7 @@
__INTRODUCED_IN(29);
/**
- * Updates the z order index for |surface_control|. Note that the z order for a surface
+ * Updates the z order index for \a surface_control. Note that the z order for a surface
* is relative to other surfaces which are siblings of this surface. The behavior of sibilings with
* the same z order is undefined.
*
@@ -267,11 +269,11 @@
__INTRODUCED_IN(29);
/**
- * Updates the AHardwareBuffer displayed for |surface_control|. If not -1, the
+ * Updates the AHardwareBuffer displayed for \a surface_control. If not -1, the
* acquire_fence_fd should be a file descriptor that is signaled when all pending work
* for the buffer is complete and the buffer can be safely read.
*
- * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible
+ * The frameworks takes ownership of the \a acquire_fence_fd passed and is responsible
* for closing it.
*
* Available since API level 29.
@@ -281,9 +283,9 @@
int acquire_fence_fd = -1) __INTRODUCED_IN(29);
/**
- * Updates the color for |surface_control|. This will make the background color for the
- * ASurfaceControl visible in transparent regions of the surface. Colors |r|, |g|,
- * and |b| must be within the range that is valid for |dataspace|. |dataspace| and |alpha|
+ * Updates the color for \a surface_control. This will make the background color for the
+ * ASurfaceControl visible in transparent regions of the surface. Colors \a r, \a g,
+ * and \a b must be within the range that is valid for \a dataspace. \a dataspace and \a alpha
* will be the dataspace and alpha set for the background color layer.
*
* Available since API level 29.
@@ -294,15 +296,15 @@
__INTRODUCED_IN(29);
/**
- * |source| the sub-rect within the buffer's content to be rendered inside the surface's area
+ * \param source The sub-rect within the buffer's content to be rendered inside the surface's area
* The surface's source rect is clipped by the bounds of its current buffer. The source rect's width
* and height must be > 0.
*
- * |destination| specifies the rect in the parent's space where this surface will be drawn. The post
+ * \param destination Specifies the rect in the parent's space where this surface will be drawn. The post
* source rect bounds are scaled to fit the destination rect. The surface's destination rect is
* clipped by the bounds of its parent. The destination rect's width and height must be > 0.
*
- * |transform| the transform applied after the source rect is applied to the buffer. This parameter
+ * \param transform The transform applied after the source rect is applied to the buffer. This parameter
* should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_*
* enum.
*
@@ -314,7 +316,9 @@
__INTRODUCED_IN(29);
-/* Parameter for ASurfaceTransaction_setBufferTransparency */
+/**
+ * Parameter for ASurfaceTransaction_setBufferTransparency().
+ */
enum {
ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0,
ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1,
@@ -360,7 +364,7 @@
/**
* Sets the alpha for the buffer. It uses a premultiplied blending.
*
- * The |alpha| must be between 0.0 and 1.0.
+ * The \a alpha must be between 0.0 and 1.0.
*
* Available since API level 29.
*/
@@ -379,10 +383,10 @@
ASurfaceControl* surface_control, ADataSpace data_space)
__INTRODUCED_IN(29);
-/*
+/**
* SMPTE ST 2086 "Mastering Display Color Volume" static metadata
*
- * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering
+ * When \a metadata is set to null, the framework does not use any smpte2086 metadata when rendering
* the surface's buffer.
*
* Available since API level 29.
@@ -392,10 +396,10 @@
struct AHdrMetadata_smpte2086* metadata)
__INTRODUCED_IN(29);
-/*
+/**
* Sets the CTA 861.3 "HDR Static Metadata Extension" static metadata on a surface.
*
- * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering
+ * When \a metadata is set to null, the framework does not use any cta861.3 metadata when rendering
* the surface's buffer.
*
* Available since API level 29.
@@ -410,24 +414,10 @@
#if __ANDROID_API__ >= 30
/**
- * Sets the intended frame rate for |surface_control|.
+ * Same as ASurfaceTransaction_setFrameRateWithSeamlessness(transaction, surface_control,
+ * frameRate, compatibility, true).
*
- * On devices that are capable of running the display at different refresh rates, the system may
- * choose a display refresh rate to better match this surface's frame rate. Usage of this API won't
- * directly affect the application's frame production pipeline. However, because the system may
- * change the display refresh rate, calls to this function may result in changes to Choreographer
- * callback timings, and changes to the time interval at which the system releases buffers back to
- * the application.
- *
- * |frameRate| is the intended frame rate of this surface, in frames per second. 0 is a special
- * value that indicates the app will accept the system's choice for the display frame rate, which is
- * the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
- * be a valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device
- * that can only run the display at 60fps.
- *
- * |compatibility| The frame rate compatibility of this surface. The compatibility value may
- * influence the system's choice of display frame rate. To specify a compatibility use the
- * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
+ * See ASurfaceTransaction_setFrameRateWithSeamlessness().
*
* Available since API level 30.
*/
@@ -437,6 +427,42 @@
#endif // __ANDROID_API__ >= 30
+#if __ANDROID_API__ >= 31
+
+/**
+ * Sets the intended frame rate for \a surface_control.
+ *
+ * On devices that are capable of running the display at different refresh rates, the system may
+ * choose a display refresh rate to better match this surface's frame rate. Usage of this API won't
+ * directly affect the application's frame production pipeline. However, because the system may
+ * change the display refresh rate, calls to this function may result in changes to Choreographer
+ * callback timings, and changes to the time interval at which the system releases buffers back to
+ * the application.
+ *
+ * \param frameRate is the intended frame rate of this surface, in frames per second. 0 is a special
+ * value that indicates the app will accept the system's choice for the display frame rate, which is
+ * the default behavior if this function isn't called. The frameRate param does <em>not</em> need to
+ * be a valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device
+ * that can only run the display at 60fps.
+ *
+ * \param compatibility The frame rate compatibility of this surface. The compatibility value may
+ * influence the system's choice of display frame rate. To specify a compatibility use the
+ * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
+ *
+ * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
+ * seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. True indicates that any frame rate changes caused by this
+ * request should be seamless. False indicates that non-seamless refresh rates are also
+ * acceptable.
+ *
+ * Available since API level 31.
+ */
+void ASurfaceTransaction_setFrameRateWithSeamlessness(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, float frameRate,
+ int8_t compatibility, bool shouldBeSeamless)
+ __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
__END_DECLS
#endif // ANDROID_SURFACE_CONTROL_H
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 678613b..ac1c736 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -378,11 +378,11 @@
}).detach();
}
- status_t setFrameRate(float frameRate, int8_t compatibility) override {
+ status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) override {
if (!ValidateFrameRate(frameRate, compatibility, "BBQSurface::setFrameRate")) {
return BAD_VALUE;
}
- return mBbq->setFrameRate(frameRate, compatibility);
+ return mBbq->setFrameRate(frameRate, compatibility, shouldBeSeamless);
}
status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId) override {
@@ -392,12 +392,12 @@
// TODO: Can we coalesce this with frame updates? Need to confirm
// no timing issues.
-status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility) {
+status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility,
+ bool shouldBeSeamless) {
std::unique_lock _lock{mMutex};
SurfaceComposerClient::Transaction t;
- return t.setFrameRate(mSurfaceControl, frameRate, compatibility)
- .apply();
+ return t.setFrameRate(mSurfaceControl, frameRate, compatibility, shouldBeSeamless).apply();
}
status_t BLASTBufferQueue::setFrameTimelineVsync(int64_t frameTimelineVsyncId) {
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 6f92233..a9fe5bf 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -1114,7 +1114,7 @@
}
virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) {
+ int8_t compatibility, bool shouldBeSeamless) {
Parcel data, reply;
status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (err != NO_ERROR) {
@@ -1140,6 +1140,12 @@
return err;
}
+ err = data.writeBool(shouldBeSeamless);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed writing bool: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
if (err != NO_ERROR) {
ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
@@ -2033,7 +2039,13 @@
ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err);
return err;
}
- status_t result = setFrameRate(surface, frameRate, compatibility);
+ bool shouldBeSeamless;
+ err = data.readBool(&shouldBeSeamless);
+ if (err != NO_ERROR) {
+ ALOGE("setFrameRate: failed to read bool: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+ status_t result = setFrameRate(surface, frameRate, compatibility, shouldBeSeamless);
reply->writeInt32(result);
return NO_ERROR;
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 9722f36..90999fa 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -59,6 +59,7 @@
frameRateSelectionPriority(-1),
frameRate(0.0f),
frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
+ shouldBeSeamless(true),
fixedTransformHint(ui::Transform::ROT_INVALID),
frameNumber(0) {
matrix.dsdx = matrix.dtdy = 1.0f;
@@ -144,6 +145,7 @@
SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
SAFE_PARCEL(output.writeFloat, frameRate);
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
+ SAFE_PARCEL(output.writeBool, shouldBeSeamless);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
SAFE_PARCEL(output.writeUint64, frameNumber);
SAFE_PARCEL(output.writeInt64, frameTimelineVsyncId);
@@ -262,6 +264,7 @@
SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
SAFE_PARCEL(input.readFloat, &frameRate);
SAFE_PARCEL(input.readByte, &frameRateCompatibility);
+ SAFE_PARCEL(input.readBool, &shouldBeSeamless);
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
SAFE_PARCEL(input.readUint64, &frameNumber);
@@ -521,6 +524,7 @@
what |= eFrameRateChanged;
frameRate = other.frameRate;
frameRateCompatibility = other.frameRateCompatibility;
+ shouldBeSeamless = other.shouldBeSeamless;
}
if (other.what & eFixedTransformHintChanged) {
what |= eFixedTransformHintChanged;
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index c1155ab..94390aa 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1443,7 +1443,8 @@
int Surface::dispatchSetFrameRate(va_list args) {
float frameRate = static_cast<float>(va_arg(args, double));
int8_t compatibility = static_cast<int8_t>(va_arg(args, int));
- return setFrameRate(frameRate, compatibility);
+ bool shouldBeSeamless = static_cast<bool>(va_arg(args, int));
+ return setFrameRate(frameRate, compatibility, shouldBeSeamless);
}
int Surface::dispatchAddCancelInterceptor(va_list args) {
@@ -2279,7 +2280,7 @@
mSurfaceListener->onBuffersDiscarded(discardedBufs);
}
-status_t Surface::setFrameRate(float frameRate, int8_t compatibility) {
+status_t Surface::setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) {
ATRACE_CALL();
ALOGV("Surface::setFrameRate");
@@ -2287,7 +2288,8 @@
return BAD_VALUE;
}
- return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility);
+ return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility,
+ shouldBeSeamless);
}
status_t Surface::setFrameTimelineVsync(int64_t frameTimelineVsyncId) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 039e900..a822598 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1474,7 +1474,8 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
- const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility) {
+ const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
+ bool shouldBeSeamless) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1487,6 +1488,7 @@
s->what |= layer_state_t::eFrameRateChanged;
s->frameRate = frameRate;
s->frameRateCompatibility = compatibility;
+ s->shouldBeSeamless = shouldBeSeamless;
return *this;
}
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 2300e81..7741d8c 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -85,7 +85,7 @@
void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height);
void flushShadowQueue() { mFlushShadowQueue = true; }
- status_t setFrameRate(float frameRate, int8_t compatibility);
+ status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId);
virtual ~BLASTBufferQueue() = default;
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 5cd9356..9e96b79 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -475,7 +475,7 @@
* Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
*/
virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) = 0;
+ int8_t compatibility, bool shouldBeSeamless) = 0;
/*
* Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index a73d9a6..d9f2806 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -218,6 +218,7 @@
// Layer frame rate and compatibility. See ANativeWindow_setFrameRate().
float frameRate;
int8_t frameRateCompatibility;
+ bool shouldBeSeamless;
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 4aa076e..82bc5c9 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -186,7 +186,7 @@
status_t getUniqueId(uint64_t* outId) const;
status_t getConsumerUsage(uint64_t* outUsage) const;
- virtual status_t setFrameRate(float frameRate, int8_t compatibility);
+ virtual status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
virtual status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId);
protected:
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 73909a3..6289c6a 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -524,7 +524,7 @@
Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
- int8_t compatibility);
+ int8_t compatibility, bool shouldBeSeamless);
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0cd3962..2392ae5 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -869,7 +869,7 @@
}
status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
- int8_t /*compatibility*/) override {
+ int8_t /*compatibility*/, bool /*shouldBeSeamless*/) override {
return NO_ERROR;
}
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index fd1793b..b406a9c 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -159,10 +159,8 @@
}
int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) {
- if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
- return -EINVAL;
- }
- return native_window_set_frame_rate(window, frameRate, compatibility);
+ return ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility,
+ /*shouldBeSeamless*/ true);
}
void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
@@ -172,6 +170,13 @@
window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
}
+int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
+ int8_t compatibility, bool shouldBeSeamless) {
+ if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+ return -EINVAL;
+ }
+ return native_window_set_frame_rate(window, frameRate, compatibility, shouldBeSeamless);
+}
/**************************************************************************************************
* vndk-stable
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 36aad2e..deea59b 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -34,6 +34,7 @@
#define ANDROID_NATIVE_WINDOW_H
#include <stdint.h>
+#include <stdbool.h>
#include <sys/cdefs.h>
#include <android/data_space.h>
@@ -256,36 +257,11 @@
};
/**
- * Sets the intended frame rate for this window.
+ * Same as ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility, true).
*
- * On devices that are capable of running the display at different refresh
- * rates, the system may choose a display refresh rate to better match this
- * window's frame rate. Usage of this API won't introduce frame rate throttling,
- * or affect other aspects of the application's frame production
- * pipeline. However, because the system may change the display refresh rate,
- * calls to this function may result in changes to Choreographer callback
- * timings, and changes to the time interval at which the system releases
- * buffers back to the application.
- *
- * Note that this only has an effect for windows presented on the display. If
- * this ANativeWindow is consumed by something other than the system compositor,
- * e.g. a media codec, this call has no effect.
+ * See ANativeWindow_setFrameRateWithSeamlessness().
*
* Available since API level 30.
- *
- * \param frameRate The intended frame rate of this window, in frames per
- * second. 0 is a special value that indicates the app will accept the system's
- * choice for the display frame rate, which is the default behavior if this
- * function isn't called. The frameRate param does <em>not</em> need to be a
- * valid refresh rate for this device's display - e.g., it's fine to pass 30fps
- * to a device that can only run the display at 60fps.
- *
- * \param compatibility The frame rate compatibility of this window. The
- * compatibility value may influence the system's choice of display refresh
- * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
- *
- * \return 0 for success, -EINVAL if the window, frame rate, or compatibility
- * value are invalid.
*/
int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility)
__INTRODUCED_IN(30);
@@ -303,6 +279,51 @@
#endif // __ANDROID_API__ >= 30
+#if __ANDROID_API__ >= 31
+
+/**
+ * Sets the intended frame rate for this window.
+ *
+ * On devices that are capable of running the display at different refresh
+ * rates, the system may choose a display refresh rate to better match this
+ * window's frame rate. Usage of this API won't introduce frame rate throttling,
+ * or affect other aspects of the application's frame production
+ * pipeline. However, because the system may change the display refresh rate,
+ * calls to this function may result in changes to Choreographer callback
+ * timings, and changes to the time interval at which the system releases
+ * buffers back to the application.
+ *
+ * Note that this only has an effect for windows presented on the display. If
+ * this ANativeWindow is consumed by something other than the system compositor,
+ * e.g. a media codec, this call has no effect.
+ *
+ * Available since API level 31.
+ *
+ * \param frameRate The intended frame rate of this window, in frames per
+ * second. 0 is a special value that indicates the app will accept the system's
+ * choice for the display frame rate, which is the default behavior if this
+ * function isn't called. The frameRate param does <em>not</em> need to be a
+ * valid refresh rate for this device's display - e.g., it's fine to pass 30fps
+ * to a device that can only run the display at 60fps.
+ *
+ * \param compatibility The frame rate compatibility of this window. The
+ * compatibility value may influence the system's choice of display refresh
+ * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
+ *
+ * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
+ * seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. True indicates that any frame rate changes caused by this
+ * request should be seamless. False indicates that non-seamless refresh rates are also
+ * acceptable.
+ *
+ * \return 0 for success, -EINVAL if the window, frame rate, or compatibility
+ * value are invalid.
+ */
+int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
+ int8_t compatibility, bool shouldBeSeamless) __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
#ifdef __cplusplus
};
#endif
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 138e08f..82d2e66 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1018,9 +1018,9 @@
}
static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
- int8_t compatibility) {
+ int8_t compatibility, bool shouldBeSeamless) {
return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
- (int)compatibility);
+ (int)compatibility, (int)shouldBeSeamless);
}
static inline int native_window_set_frame_timeline_vsync(struct ANativeWindow* window,
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 1b5d20d..de48ec2 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -46,6 +46,7 @@
ANativeWindow_setBuffersTransform;
ANativeWindow_setDequeueTimeout; # apex # introduced=30
ANativeWindow_setFrameRate; # introduced=30
+ ANativeWindow_setFrameRateWithSeamlessness; # introduced=31
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
ANativeWindow_setUsage; # llndk
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index fff7854..8c5f0e6 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -252,8 +252,8 @@
// initialize the renderer while GL is current
std::unique_ptr<SkiaGLRenderEngine> engine =
- std::make_unique<SkiaGLRenderEngine>(args, display, config, ctxt, placeholder,
- protectedContext, protectedPlaceholder);
+ std::make_unique<SkiaGLRenderEngine>(args, display, ctxt, placeholder, protectedContext,
+ protectedPlaceholder);
ALOGI("OpenGL ES informations:");
ALOGI("vendor : %s", extensions.getVendor());
@@ -306,38 +306,52 @@
}
SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
- EGLConfig config, EGLContext ctxt, EGLSurface placeholder,
+ EGLContext ctxt, EGLSurface placeholder,
EGLContext protectedContext, EGLSurface protectedPlaceholder)
: mEGLDisplay(display),
- mEGLConfig(config),
mEGLContext(ctxt),
mPlaceholderSurface(placeholder),
mProtectedEGLContext(protectedContext),
mProtectedPlaceholderSurface(protectedPlaceholder),
mUseColorManagement(args.useColorManagement) {
- // Suppress unused field warnings for things we definitely will need/use
- // These EGL fields will all be needed for toggling between protected & unprotected contexts
- // Or we need different RE instances for that
- (void)mEGLDisplay;
- (void)mEGLConfig;
- (void)mEGLContext;
- (void)mPlaceholderSurface;
- (void)mProtectedEGLContext;
- (void)mProtectedPlaceholderSurface;
-
sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
LOG_ALWAYS_FATAL_IF(!glInterface.get());
GrContextOptions options;
options.fPreferExternalImagesOverES3 = true;
options.fDisableDistanceFieldPaths = true;
- mGrContext = GrDirectContext::MakeGL(std::move(glInterface), options);
+ mGrContext = GrDirectContext::MakeGL(glInterface, options);
+ if (useProtectedContext(true)) {
+ mProtectedGrContext = GrDirectContext::MakeGL(glInterface, options);
+ useProtectedContext(false);
+ }
if (args.supportsBackgroundBlur) {
mBlurFilter = new BlurFilter();
}
}
+bool SkiaGLRenderEngine::supportsProtectedContent() const {
+ return mProtectedEGLContext != EGL_NO_CONTEXT;
+}
+
+bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) {
+ if (useProtectedContext == mInProtectedContext) {
+ return true;
+ }
+ if (useProtectedContext && supportsProtectedContent()) {
+ return false;
+ }
+ const EGLSurface surface =
+ useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface;
+ const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
+ const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
+ if (success) {
+ mInProtectedContext = useProtectedContext;
+ }
+ return success;
+}
+
base::unique_fd SkiaGLRenderEngine::flush() {
ATRACE_CALL();
if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) {
@@ -471,22 +485,23 @@
return BAD_VALUE;
}
+ auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext;
+ auto cache = mInProtectedContext ? mProtectedSurfaceCache : mSurfaceCache;
AHardwareBuffer_Desc bufferDesc;
AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc);
-
LOG_ALWAYS_FATAL_IF(!hasUsage(bufferDesc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE),
"missing usage");
sk_sp<SkSurface> surface;
if (useFramebufferCache) {
- auto iter = mSurfaceCache.find(buffer->getId());
- if (iter != mSurfaceCache.end()) {
+ auto iter = cache.find(buffer->getId());
+ if (iter != cache.end()) {
ALOGV("Cache hit!");
surface = iter->second;
}
}
if (!surface) {
- surface = SkSurface::MakeFromAHardwareBuffer(mGrContext.get(), buffer->toAHardwareBuffer(),
+ surface = SkSurface::MakeFromAHardwareBuffer(grContext.get(), buffer->toAHardwareBuffer(),
GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
mUseColorManagement
? toColorSpace(display.outputDataspace)
@@ -494,7 +509,7 @@
nullptr);
if (useFramebufferCache && surface) {
ALOGD("Adding to cache");
- mSurfaceCache.insert({buffer->getId(), surface});
+ cache.insert({buffer->getId(), surface});
}
}
if (!surface) {
@@ -705,7 +720,7 @@
} else {
ATRACE_BEGIN("Submit(sync=false)");
}
- bool success = mGrContext->submit(requireSync);
+ bool success = grContext->submit(requireSync);
ATRACE_END();
if (!success) {
ALOGE("Failed to flush RenderEngine commands");
@@ -897,6 +912,7 @@
void SkiaGLRenderEngine::cleanFramebufferCache() {
mSurfaceCache.clear();
+ mProtectedSurfaceCache.clear();
}
} // namespace skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 43db3b1..965cb41 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -40,8 +40,8 @@
class SkiaGLRenderEngine : public skia::SkiaRenderEngine {
public:
static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args);
- SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
- EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext,
+ SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt,
+ EGLSurface placeholder, EGLContext protectedContext,
EGLSurface protectedPlaceholder);
~SkiaGLRenderEngine() override{};
@@ -51,6 +51,9 @@
const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
void cleanFramebufferCache() override;
+ bool isProtected() const override { return mInProtectedContext; }
+ bool supportsProtectedContent() const override;
+ bool useProtectedContext(bool useProtectedContext) override;
protected:
void dump(std::string& /*result*/) override{};
@@ -81,7 +84,6 @@
const SkMatrix& drawTransform, sk_sp<SkSurface> blurrendSurface);
EGLDisplay mEGLDisplay;
- EGLConfig mEGLConfig;
EGLContext mEGLContext;
EGLSurface mPlaceholderSurface;
EGLContext mProtectedEGLContext;
@@ -100,9 +102,14 @@
sp<Fence> mLastDrawFence;
+ // Graphics context used for creating surfaces and submitting commands
sk_sp<GrDirectContext> mGrContext;
+ // Same as above, but for protected content (eg. DRM)
+ sk_sp<GrDirectContext> mProtectedGrContext;
std::unordered_map<uint64_t, sk_sp<SkSurface>> mSurfaceCache;
+ std::unordered_map<uint64_t, sk_sp<SkSurface>> mProtectedSurfaceCache;
+ bool mInProtectedContext = false;
};
} // namespace skia
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1911a0a..b6b754b 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1687,8 +1687,9 @@
crop.bottom);
if (layerState.frameRate.rate != 0 ||
layerState.frameRate.type != FrameRateCompatibility::Default) {
- StringAppendF(&result, "% 6.2ffps %15s", layerState.frameRate.rate,
- frameRateCompatibilityString(layerState.frameRate.type).c_str());
+ StringAppendF(&result, "% 6.2ffps %15s seamless=%d", layerState.frameRate.rate,
+ frameRateCompatibilityString(layerState.frameRate.type).c_str(),
+ layerState.frameRate.shouldBeSeamless);
} else {
StringAppendF(&result, " ");
}
@@ -2750,6 +2751,12 @@
// ---------------------------------------------------------------------------
+std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
+ return stream << "{rate=" << rate.rate
+ << " type=" << Layer::frameRateCompatibilityString(rate.type)
+ << " shouldBeSeamless=" << rate.shouldBeSeamless << "}";
+}
+
}; // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index b1ab9ec..1a784aa 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -153,12 +153,15 @@
struct FrameRate {
float rate;
FrameRateCompatibility type;
+ bool shouldBeSeamless;
- FrameRate() : rate(0), type(FrameRateCompatibility::Default) {}
- FrameRate(float rate, FrameRateCompatibility type) : rate(rate), type(type) {}
+ FrameRate() : rate(0), type(FrameRateCompatibility::Default), shouldBeSeamless(true) {}
+ FrameRate(float rate, FrameRateCompatibility type, bool shouldBeSeamless = true)
+ : rate(rate), type(type), shouldBeSeamless(shouldBeSeamless) {}
bool operator==(const FrameRate& other) const {
- return rate == other.rate && type == other.type;
+ return rate == other.rate && type == other.type &&
+ shouldBeSeamless == other.shouldBeSeamless;
}
bool operator!=(const FrameRate& other) const { return !(*this == other); }
@@ -1126,4 +1129,6 @@
const std::vector<BlurRegion>& getBlurRegions() const;
};
+std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 36433c2..28af930 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -125,12 +125,23 @@
return LayerVoteType::NoVote;
}
}();
- summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f,
- layerFocused});
+ summary.push_back(
+ RefreshRateConfigs::LayerRequirement{.name = layer->getName(),
+ .vote = voteType,
+ .desiredRefreshRate = frameRate.rate,
+ .shouldBeSeamless =
+ frameRate.shouldBeSeamless,
+ .weight = 1.0f,
+ .focused = layerFocused});
} else if (recent) {
- summary.push_back({layer->getName(), LayerVoteType::Heuristic,
- info->getRefreshRate(now),
- /* weight */ 1.0f, layerFocused});
+ summary.push_back(
+ RefreshRateConfigs::LayerRequirement{.name = layer->getName(),
+ .vote = LayerVoteType::Heuristic,
+ .desiredRefreshRate =
+ info->getRefreshRate(now),
+ .shouldBeSeamless = true,
+ .weight = 1.0f,
+ .focused = layerFocused});
}
if (CC_UNLIKELY(mTraceEnabled)) {
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 37e67e1..a63ccc1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -130,9 +130,9 @@
ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
frameRateSelectionPriority, layerFocused ? "" : "not");
- const auto [type, refreshRate] = info->getRefreshRate(now);
+ const auto vote = info->getRefreshRateVote(now);
// Skip NoVote layer as those don't have any requirements
- if (type == LayerHistory::LayerVoteType::NoVote) {
+ if (vote.type == LayerHistory::LayerVoteType::NoVote) {
continue;
}
@@ -144,10 +144,11 @@
const float layerArea = transformed.getWidth() * transformed.getHeight();
float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
- summary.push_back({strong->getName(), type, refreshRate, weight, layerFocused});
+ summary.push_back({strong->getName(), vote.type, vote.fps, vote.shouldBeSeamless, weight,
+ layerFocused});
if (CC_UNLIKELY(mTraceEnabled)) {
- trace(layer, *info, type, static_cast<int>(std::round(refreshRate)));
+ trace(layer, *info, vote.type, static_cast<int>(std::round(vote.fps)));
}
}
@@ -178,7 +179,7 @@
if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
- info->setLayerVote(type, frameRate.rate);
+ info->setLayerVote({type, frameRate.rate, frameRate.shouldBeSeamless});
} else {
info->resetLayerVote();
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index 44f20d0..94e7e20 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -198,10 +198,10 @@
: std::make_optional(mLastRefreshRate.reported);
}
-std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
+LayerInfoV2::LayerVote LayerInfoV2::getRefreshRateVote(nsecs_t now) {
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
- return {mLayerVote.type, mLayerVote.fps};
+ return mLayerVote;
}
if (isAnimating(now)) {
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 33dc66f..2305bc3 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -56,6 +56,13 @@
friend class LayerHistoryTestV2;
public:
+ // Holds information about the layer vote
+ struct LayerVote {
+ LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
+ float fps = 0.0f;
+ bool shouldBeSeamless = true;
+ };
+
static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
@@ -76,7 +83,7 @@
// Sets an explicit layer vote. This usually comes directly from the application via
// ANativeWindow_setFrameRate API
- void setLayerVote(LayerHistory::LayerVoteType type, float fps) { mLayerVote = {type, fps}; }
+ void setLayerVote(LayerVote vote) { mLayerVote = vote; }
// Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
// This is used for layers that called to setLayerVote() and then removed the vote, so that the
@@ -84,9 +91,9 @@
void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
// Resets the layer vote to its default.
- void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f}; }
+ void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f, true}; }
- std::pair<LayerHistory::LayerVoteType, float> getRefreshRate(nsecs_t now);
+ LayerVote getRefreshRateVote(nsecs_t now);
// Return the last updated time. If the present time is farther in the future than the
// updated time, the updated time is the present time.
@@ -130,12 +137,6 @@
bool animatingOrInfrequent = false;
};
- // Holds information about the layer vote
- struct LayerVote {
- LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
- float fps = 0.0f;
- };
-
// Class to store past calculated refresh rate and determine whether
// the refresh rate calculated is consistent with past values
class RefreshRateHistory {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 150f925..b872d7a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -31,6 +31,12 @@
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
+std::string RefreshRate::toString() const {
+ return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
+ getConfigId().value(), hwcConfig->getId(), getFps(),
+ hwcConfig->getWidth(), hwcConfig->getHeight(), getConfigGroup());
+}
+
std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) {
switch (vote) {
case LayerVoteType::NoVote:
@@ -125,7 +131,7 @@
const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
- ALOGV("getRefreshRateForContent %zu layers", layers.size());
+ ALOGV("getBestRefreshRate %zu layers", layers.size());
if (outSignalsConsidered) *outSignalsConsidered = {};
const auto setTouchConsidered = [&] {
@@ -148,6 +154,7 @@
int explicitDefaultVoteLayers = 0;
int explicitExactOrMultipleVoteLayers = 0;
float maxExplicitWeight = 0;
+ int seamedLayers = 0;
for (const auto& layer : layers) {
if (layer.vote == LayerVoteType::NoVote) {
noVoteLayers++;
@@ -162,6 +169,10 @@
explicitExactOrMultipleVoteLayers++;
maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
}
+
+ if (!layer.shouldBeSeamless) {
+ seamedLayers++;
+ }
}
const bool hasExplicitVoteLayers =
@@ -206,6 +217,8 @@
scores.emplace_back(refreshRate, 0.0f);
}
+ const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig);
+
for (const auto& layer : layers) {
ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
layerVoteTypeString(layer.vote).c_str(), layer.weight);
@@ -216,6 +229,30 @@
auto weight = layer.weight;
for (auto i = 0u; i < scores.size(); i++) {
+ // If there are no layers with shouldBeSeamless=false and the current
+ // config group is different from the default one, this means a layer with
+ // shouldBeSeamless=false has just disappeared and we should switch back to
+ // the default config group.
+ const bool isSeamlessSwitch = seamedLayers > 0
+ ? scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup()
+ : scores[i].first->getConfigGroup() == defaultConfig->getConfigGroup();
+
+ if (layer.shouldBeSeamless && !isSeamlessSwitch) {
+ ALOGV("%s (weight %.2f) ignores %s (group=%d) to avoid non-seamless switch."
+ "Current config = %s",
+ layer.name.c_str(), weight, scores[i].first->name.c_str(),
+ scores[i].first->getConfigGroup(), mCurrentRefreshRate->toString().c_str());
+ continue;
+ }
+
+ if (!layer.shouldBeSeamless && !isSeamlessSwitch && !layer.focused) {
+ ALOGV("%s (weight %.2f) ignores %s (group=%d) because it's not focused"
+ " and the switch is going to be seamed. Current config = %s",
+ layer.name.c_str(), weight, scores[i].first->name.c_str(),
+ scores[i].first->getConfigGroup(), mCurrentRefreshRate->toString().c_str());
+ continue;
+ }
+
bool inPrimaryRange =
scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
@@ -292,10 +329,13 @@
return 1.0f / iter;
}();
+ // Slightly prefer seamless switches.
+ constexpr float kSeamedSwitchPenalty = 0.95f;
+ const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod,
scores[i].first->name.c_str(), layerScore);
- scores[i].second += weight * layerScore;
+ scores[i].second += weight * layerScore * seamlessness;
continue;
}
}
@@ -367,6 +407,15 @@
}
const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
+ for (auto refreshRate : mPrimaryRefreshRates) {
+ if (mCurrentRefreshRate->getConfigGroup() == refreshRate->getConfigGroup()) {
+ return *refreshRate;
+ }
+ }
+ ALOGE("Can't find min refresh rate by policy with the same config group"
+ " as the current config %s",
+ mCurrentRefreshRate->toString().c_str());
+ // Defaulting to the lowest refresh rate
return *mPrimaryRefreshRates.front();
}
@@ -376,6 +425,16 @@
}
const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
+ for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) {
+ const auto& refreshRate = (**it);
+ if (mCurrentRefreshRate->getConfigGroup() == refreshRate.getConfigGroup()) {
+ return refreshRate;
+ }
+ }
+ ALOGE("Can't find max refresh rate by policy with the same config group"
+ " as the current config %s",
+ mCurrentRefreshRate->toString().c_str());
+ // Defaulting to the highest refresh rate
return *mPrimaryRefreshRates.back();
}
@@ -414,7 +473,7 @@
const float fps = 1e9f / config->getVsyncPeriod();
mRefreshRates.emplace(configId,
std::make_unique<RefreshRate>(configId, config,
- base::StringPrintf("%.0ffps", fps), fps,
+ base::StringPrintf("%.2ffps", fps), fps,
RefreshRate::ConstructorTag(0)));
if (configId == currentConfigId) {
mCurrentRefreshRate = mRefreshRates.at(configId).get();
@@ -660,4 +719,26 @@
return static_cast<int>(numPeriods);
}
+void RefreshRateConfigs::dump(std::string& result) const {
+ std::lock_guard lock(mLock);
+ base::StringAppendF(&result, "DesiredDisplayConfigSpecs (DisplayManager): %s\n\n",
+ mDisplayManagerPolicy.toString().c_str());
+ scheduler::RefreshRateConfigs::Policy currentPolicy = *getCurrentPolicyLocked();
+ if (mOverridePolicy && currentPolicy != mDisplayManagerPolicy) {
+ base::StringAppendF(&result, "DesiredDisplayConfigSpecs (Override): %s\n\n",
+ currentPolicy.toString().c_str());
+ }
+
+ auto config = mCurrentRefreshRate->hwcConfig;
+ base::StringAppendF(&result, "Current config: %s\n", mCurrentRefreshRate->toString().c_str());
+
+ result.append("Refresh rates:\n");
+ for (const auto& [id, refreshRate] : mRefreshRates) {
+ config = refreshRate->hwcConfig;
+ base::StringAppendF(&result, "\t%s\n", refreshRate->toString().c_str());
+ }
+
+ result.append("\n");
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 8ff92a0..3159352 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -86,6 +86,8 @@
bool operator==(const RefreshRate& other) const { return !(*this != other); }
+ std::string toString() const;
+
private:
friend RefreshRateConfigs;
friend class RefreshRateConfigsTest;
@@ -216,6 +218,8 @@
LayerVoteType vote = LayerVoteType::NoVote;
// Layer's desired refresh rate, if applicable.
float desiredRefreshRate = 0.0f;
+ // If a seamless mode switch is required.
+ bool shouldBeSeamless = true;
// Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
// would have on choosing the refresh rate.
float weight = 0.0f;
@@ -318,6 +322,8 @@
// Returns a divider for the current refresh rate
int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock);
+ void dump(std::string& result) const EXCLUDES(mLock);
+
private:
friend class RefreshRateConfigsTest;
diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h
index e8ca0ba..6a60257 100644
--- a/services/surfaceflinger/Scheduler/StrongTyping.h
+++ b/services/surfaceflinger/Scheduler/StrongTyping.h
@@ -70,6 +70,10 @@
T const& value() const { return mValue; }
T& value() { return mValue; }
+ friend std::ostream& operator<<(std::ostream& os, const StrongTyping<T, W, Ability...>& value) {
+ return os << value.value();
+ }
+
private:
T mValue;
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 27facd6..91f050c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3781,7 +3781,8 @@
"SurfaceFlinger::setClientStateLocked") &&
layer->setFrameRate(Layer::FrameRate(s.frameRate,
Layer::FrameRate::convertCompatibility(
- s.frameRateCompatibility)))) {
+ s.frameRateCompatibility),
+ s.shouldBeSeamless))) {
flags |= eTraversalNeeded;
}
}
@@ -4373,16 +4374,10 @@
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
- scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy();
- StringAppendF(&result, "DesiredDisplayConfigSpecs (DisplayManager): %s\n\n",
- policy.toString().c_str());
+ mRefreshRateConfigs->dump(result);
+
StringAppendF(&result, "(config override by backdoor: %s)\n\n",
mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
- scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
- if (currentPolicy != policy) {
- StringAppendF(&result, "DesiredDisplayConfigSpecs (Override): %s\n\n",
- currentPolicy.toString().c_str());
- }
mScheduler->dump(mAppConnectionHandle, result);
mScheduler->dumpVsync(result);
@@ -6141,7 +6136,7 @@
}
status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) {
+ int8_t compatibility, bool shouldBeSeamless) {
if (!ValidateFrameRate(frameRate, compatibility, "SurfaceFlinger::setFrameRate")) {
return BAD_VALUE;
}
@@ -6154,10 +6149,10 @@
ALOGE("Attempt to set frame rate on a layer that no longer exists");
return BAD_VALUE;
}
-
if (layer->setFrameRate(
Layer::FrameRate(frameRate,
- Layer::FrameRate::convertCompatibility(compatibility)))) {
+ Layer::FrameRate::convertCompatibility(compatibility),
+ shouldBeSeamless))) {
setTransactionFlags(eTraversalNeeded);
}
} else {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a821d44..9666f14 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -600,7 +600,7 @@
status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ, float lightRadius) override;
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility) override;
+ int8_t compatibility, bool shouldBeSeamless) override;
status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface,
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index da71dad..b87c734 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -123,7 +123,7 @@
}
virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
- int32_t bufferWidth, int32_t bufferHeight) {
+ uint32_t bufferWidth, uint32_t bufferHeight) {
ANativeWindow_Buffer buffer;
ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
TransactionUtils::fillANativeWindowBufferColor(buffer,
@@ -145,7 +145,7 @@
}
void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color,
- int32_t bufferWidth, int32_t bufferHeight) {
+ uint32_t bufferWidth, uint32_t bufferHeight) {
switch (mLayerType) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
diff --git a/services/surfaceflinger/tests/SetFrameRate_test.cpp b/services/surfaceflinger/tests/SetFrameRate_test.cpp
index 02ba9e2..d1bed0c 100644
--- a/services/surfaceflinger/tests/SetFrameRate_test.cpp
+++ b/services/surfaceflinger/tests/SetFrameRate_test.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <system/window.h>
#include <thread>
@@ -50,8 +46,8 @@
}
}
- const int mLayerWidth = 32;
- const int mLayerHeight = 32;
+ const uint32_t mLayerWidth = 32;
+ const uint32_t mLayerHeight = 32;
sp<SurfaceControl> mLayer;
uint32_t mLayerType;
};
@@ -59,26 +55,27 @@
TEST_F(SetFrameRateTest, BufferQueueLayerSetFrameRate) {
CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferQueue);
native_window_set_frame_rate(mLayer->getSurface().get(), 100.f,
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT);
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true);
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
Transaction()
- .setFrameRate(mLayer, 200.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT)
+ .setFrameRate(mLayer, 200.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true)
.apply();
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
native_window_set_frame_rate(mLayer->getSurface().get(), 300.f,
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT);
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true);
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
}
TEST_F(SetFrameRateTest, BufferStateLayerSetFrameRate) {
CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferState);
Transaction()
- .setFrameRate(mLayer, 400.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT)
+ .setFrameRate(mLayer, 400.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ /* shouldBeSeamless */ true)
.apply();
ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::GREEN));
}
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 01badf4..a361b1e 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -98,14 +98,14 @@
outTransformHint, format);
}
- void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
- int32_t bufferHeight) {
+ void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, uint32_t bufferWidth,
+ uint32_t bufferHeight) {
ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color,
bufferWidth, bufferHeight));
}
- void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
- int32_t bufferHeight, const Color& topLeft, const Color& topRight,
+ void fillLayerQuadrant(const sp<SurfaceControl>& layer, uint32_t bufferWidth,
+ uint32_t bufferHeight, const Color& topLeft, const Color& topRight,
const Color& bottomLeft, const Color& bottomRight) {
ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer,
bufferWidth, bufferHeight,
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index cb376cd..3b50321 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -78,7 +78,7 @@
for (auto& [weak, info] : history().mLayerInfos) {
if (auto strong = weak.promote(); strong && strong.get() == layer) {
info->setDefaultLayerVote(vote);
- info->setLayerVote(vote, 0);
+ info->setLayerVote({vote, 0, false});
return;
}
}
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 4762fd4..df76110 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -57,6 +57,8 @@
static inline const HwcConfigIndexType HWC_CONFIG_ID_72 = HwcConfigIndexType(2);
static inline const HwcConfigIndexType HWC_CONFIG_ID_120 = HwcConfigIndexType(3);
static inline const HwcConfigIndexType HWC_CONFIG_ID_30 = HwcConfigIndexType(4);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_25 = HwcConfigIndexType(5);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_50 = HwcConfigIndexType(6);
// Test configs
std::shared_ptr<const HWC2::Display::Config> mConfig60 =
@@ -77,8 +79,16 @@
createConfig(HWC_CONFIG_ID_120, 1, static_cast<int64_t>(1e9f / 120));
std::shared_ptr<const HWC2::Display::Config> mConfig30 =
createConfig(HWC_CONFIG_ID_30, 0, static_cast<int64_t>(1e9f / 30));
+ std::shared_ptr<const HWC2::Display::Config> mConfig30DifferentGroup =
+ createConfig(HWC_CONFIG_ID_30, 1, static_cast<int64_t>(1e9f / 30));
+ std::shared_ptr<const HWC2::Display::Config> mConfig25DifferentGroup =
+ createConfig(HWC_CONFIG_ID_25, 1, static_cast<int64_t>(1e9f / 25));
+ std::shared_ptr<const HWC2::Display::Config> mConfig50 =
+ createConfig(HWC_CONFIG_ID_50, 0, static_cast<int64_t>(1e9f / 50));
// Test device configurations
+ // The positions of the configs in the arrays below MUST match their IDs. For example,
+ // the first config should always be 60Hz, the second 90Hz etc.
std::vector<std::shared_ptr<const HWC2::Display::Config>> m60OnlyConfigDevice = {mConfig60};
std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90Device = {mConfig60, mConfig90};
std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90DeviceWithDifferentGroups =
@@ -104,6 +114,14 @@
{mConfig60, mConfig90, mConfig72, mConfig120DifferentGroup, mConfig30};
std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_90Device =
{mConfig60, mConfig90, mConfig72DifferentGroup, mConfig120DifferentGroup, mConfig30};
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> m25_30_50_60Device =
+ {mConfig60,
+ mConfig90,
+ mConfig72DifferentGroup,
+ mConfig120DifferentGroup,
+ mConfig30DifferentGroup,
+ mConfig25DifferentGroup,
+ mConfig50};
// Expected RefreshRate objects
RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, "60fps", 60,
@@ -292,8 +310,8 @@
/*currentConfigId=*/HWC_CONFIG_ID_60);
const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
- return {{"testLayer", LayerVoteType::Heuristic, refreshRate, /*weight*/ 1.0f,
- /*focused*/ false}};
+ return {{"testLayer", LayerVoteType::Heuristic, refreshRate, /*shouldBeSeamless*/ true,
+ /*weight*/ 1.0f, /*focused*/ false}};
};
EXPECT_EQ(mExpected90Config,
@@ -1245,7 +1263,9 @@
auto& layer = layers[0];
layer.vote = LayerVoteType::ExplicitDefault;
layer.desiredRefreshRate = 90.0f;
+ layer.shouldBeSeamless = false;
layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
ASSERT_EQ(HWC_CONFIG_ID_60,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
@@ -1258,6 +1278,104 @@
ASSERT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getConfigId());
+
+ // Verify that we won't change the group if seamless switch is required.
+ layer.shouldBeSeamless = true;
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+
+ // At this point the default config in the DisplayManager policy with be 60Hz.
+ // Verify that if the current config is in another group and there are no layers with
+ // shouldBeSeamless=false we'll go back to the default group.
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ layer.desiredRefreshRate = 60.0f;
+ layer.name = "60Hz ExplicitDefault";
+ layer.shouldBeSeamless = true;
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+
+ // If there's a layer with shouldBeSeamless=false, another layer with shouldBeSeamless=true
+ // can't change the config group.
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ auto layer2 = LayerRequirement{.weight = 0.5f};
+ layer2.vote = LayerVoteType::ExplicitDefault;
+ layer2.desiredRefreshRate = 90.0f;
+ layer2.name = "90Hz ExplicitDefault";
+ layer2.shouldBeSeamless = false;
+ layer2.focused = false;
+ layers.push_back(layer2);
+ ASSERT_EQ(HWC_CONFIG_ID_90,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+}
+
+TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ // Allow group switching.
+ RefreshRateConfigs::Policy policy;
+ policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ layer.desiredRefreshRate = 60.0f;
+ layer.shouldBeSeamless = false;
+ layer.name = "60Hz ExplicitExactOrMultiple";
+ layer.focused = true;
+
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
+ ASSERT_EQ(HWC_CONFIG_ID_120,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+}
+
+TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m25_30_50_60Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ // Allow group switching.
+ RefreshRateConfigs::Policy policy;
+ policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ auto layers = std::vector<
+ LayerRequirement>{LayerRequirement{.name = "60Hz ExplicitDefault",
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = 60.0f,
+ .shouldBeSeamless = false,
+ .weight = 0.5f,
+ .focused = false},
+ LayerRequirement{.name = "25Hz ExplicitExactOrMultiple",
+ .vote = LayerVoteType::ExplicitExactOrMultiple,
+ .desiredRefreshRate = 25.0f,
+ .shouldBeSeamless = true,
+ .weight = 1.0f,
+ .focused = true}};
+ auto& seamedLayer = layers[0];
+
+ ASSERT_EQ(HWC_CONFIG_ID_50,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
+
+ seamedLayer.name = "30Hz ExplicitDefault", seamedLayer.desiredRefreshRate = 30.0f;
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_30);
+
+ ASSERT_EQ(HWC_CONFIG_ID_25,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getConfigId());
}
TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index de66f8f..d0bb9e2 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -108,7 +108,7 @@
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(0u, times.count("90fps"));
+ EXPECT_EQ(0u, times.count("90.00fps"));
mRefreshRateStats->setConfigMode(CONFIG_ID_0);
mRefreshRateStats->setPowerMode(PowerMode::ON);
@@ -116,15 +116,15 @@
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- ASSERT_EQ(1u, times.count("90fps"));
- EXPECT_LT(0, times["90fps"]);
+ ASSERT_EQ(1u, times.count("90.00fps"));
+ EXPECT_LT(0, times["90.00fps"]);
mRefreshRateStats->setPowerMode(PowerMode::DOZE);
- int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ int ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
mRefreshRateStats->setConfigMode(CONFIG_ID_0);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
@@ -133,7 +133,7 @@
// Because the power mode is not PowerMode::ON, switching the config
// does not update refresh rates that come from the config.
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
}
TEST_F(RefreshRateStatsTest, twoConfigsTest) {
@@ -163,53 +163,53 @@
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- ASSERT_EQ(1u, times.count("90fps"));
- EXPECT_LT(0, times["90fps"]);
+ ASSERT_EQ(1u, times.count("90.00fps"));
+ EXPECT_LT(0, times["90.00fps"]);
// When power mode is normal, time for configs updates.
mRefreshRateStats->setConfigMode(CONFIG_ID_1);
- int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ int ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- ASSERT_EQ(1u, times.count("60fps"));
- EXPECT_LT(0, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ ASSERT_EQ(1u, times.count("60.00fps"));
+ EXPECT_LT(0, times["60.00fps"]);
mRefreshRateStats->setConfigMode(CONFIG_ID_0);
- int sixty = mRefreshRateStats->getTotalTimes()["60fps"];
+ int sixty = mRefreshRateStats->getTotalTimes()["60.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_LT(ninety, times["90fps"]);
- EXPECT_EQ(sixty, times["60fps"]);
+ EXPECT_LT(ninety, times["90.00fps"]);
+ EXPECT_EQ(sixty, times["60.00fps"]);
mRefreshRateStats->setConfigMode(CONFIG_ID_1);
- ninety = mRefreshRateStats->getTotalTimes()["90fps"];
+ ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- EXPECT_LT(sixty, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ EXPECT_LT(sixty, times["60.00fps"]);
// Because the power mode is not PowerMode::ON, switching the config
// does not update refresh rates that come from the config.
mRefreshRateStats->setPowerMode(PowerMode::DOZE);
mRefreshRateStats->setConfigMode(CONFIG_ID_0);
- sixty = mRefreshRateStats->getTotalTimes()["60fps"];
+ sixty = mRefreshRateStats->getTotalTimes()["60.00fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- EXPECT_EQ(sixty, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ EXPECT_EQ(sixty, times["60.00fps"]);
mRefreshRateStats->setConfigMode(CONFIG_ID_1);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90fps"]);
- EXPECT_EQ(sixty, times["60fps"]);
+ EXPECT_EQ(ninety, times["90.00fps"]);
+ EXPECT_EQ(sixty, times["60.00fps"]);
}
} // namespace
} // namespace scheduler
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index afb3004..7097e7a 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -15,7 +15,6 @@
sharedLibraries = [
"libbase",
"libcutils",
- "libgtest_prod",
"libgui",
"liblog",
"libpdx_default_transport",
@@ -48,6 +47,7 @@
cc_binary {
srcs: ["bufferhubd.cpp"],
+ system_ext_specific: true,
cflags: [
"-DLOG_TAG=\"bufferhubd\"",
"-DTRACE=0",