Merge "Do not link tests to libinputreader"
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
index 194cd2f..9f53a57 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
@@ -37,6 +37,7 @@
ERROR_JPEGR_INVALID_NULL_PTR = JPEGR_IO_ERROR_BASE - 2,
ERROR_JPEGR_RESOLUTION_MISMATCH = JPEGR_IO_ERROR_BASE - 3,
ERROR_JPEGR_BUFFER_TOO_SMALL = JPEGR_IO_ERROR_BASE - 4,
+ ERROR_JPEGR_INVALID_COLORGAMUT = JPEGR_IO_ERROR_BASE - 5,
JPEGR_RUNTIME_ERROR_BASE = -20000,
ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1,
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index b2ca481..d258f80 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -22,11 +22,17 @@
namespace android::recoverymap {
typedef enum {
- JPEGR_COLORSPACE_UNSPECIFIED,
- JPEGR_COLORSPACE_BT709,
- JPEGR_COLORSPACE_P3,
- JPEGR_COLORSPACE_BT2100,
-} jpegr_color_space;
+ JPEGR_COLORGAMUT_UNSPECIFIED,
+ JPEGR_COLORGAMUT_BT709,
+ JPEGR_COLORGAMUT_P3,
+ JPEGR_COLORGAMUT_BT2100,
+} jpegr_color_gamut;
+
+// Transfer functions as defined for XMP metadata
+typedef enum {
+ JPEGR_TF_HLG = 0,
+ JPEGR_TF_PQ = 1,
+} jpegr_transfer_function;
/*
* Holds information for uncompressed image or recovery map.
@@ -38,8 +44,8 @@
int width;
// Height of the recovery map or image in pixels.
int height;
- // Color space.
- jpegr_color_space colorSpace;
+ // Color gamut.
+ jpegr_color_gamut colorGamut;
};
/*
@@ -50,8 +56,8 @@
void* data;
// Data length.
int length;
- // Color space.
- jpegr_color_space colorSpace;
+ // Color gamut.
+ jpegr_color_gamut colorGamut;
};
/*
@@ -64,9 +70,51 @@
int length;
};
+struct chromaticity_coord {
+ float x;
+ float y;
+};
+
+
+struct st2086_metadata {
+ // xy chromaticity coordinate of the red primary of the mastering display
+ chromaticity_coord redPrimary;
+ // xy chromaticity coordinate of the green primary of the mastering display
+ chromaticity_coord greenPrimary;
+ // xy chromaticity coordinate of the blue primary of the mastering display
+ chromaticity_coord bluePrimary;
+ // xy chromaticity coordinate of the white point of the mastering display
+ chromaticity_coord whitePoint;
+ // Maximum luminance in nits of the mastering display
+ uint32_t maxLuminance;
+ // Minimum luminance in nits of the mastering display
+ float minLuminance;
+};
+
+struct hdr10_metadata {
+ // Mastering display color volume
+ st2086_metadata st2086Metadata;
+ // Max frame average light level in nits
+ float maxFALL;
+ // Max content light level in nits
+ float maxCLL;
+};
+
+struct jpegr_metadata {
+ // JPEG/R version
+ uint32_t version;
+ // Range scaling factor for the map
+ float rangeScalingFactor;
+ // The transfer function for decoding the HDR representation of the image
+ jpegr_transfer_function transferFunction;
+ // HDR10 metadata, only applicable for transferFunction of JPEGR_TF_PQ
+ hdr10_metadata hdr10Metadata;
+};
+
typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
typedef struct jpegr_compressed_struct* jr_compressed_ptr;
typedef struct jpegr_exif_struct* jr_exif_ptr;
+typedef struct jpegr_metadata* jr_metadata_ptr;
class RecoveryMap {
public:
@@ -75,9 +123,10 @@
*
* Generate recovery map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
* the recovery map to the end of the compressed JPEG. HDR and SDR inputs must be the same
- * resolution and color space.
+ * resolution.
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image
* @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
* the highest quality
@@ -86,6 +135,7 @@
*/
status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest,
int quality,
jr_exif_ptr exif);
@@ -100,12 +150,14 @@
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image
* @return NO_ERROR if encoding succeeds, error code if error occurs.
*/
status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest);
/*
@@ -115,27 +167,28 @@
*
* Decode the compressed 8-bit JPEG image to YUV SDR, generate recovery map from the HDR input
* and the decoded SDR result, append the recovery map to the end of the compressed JPEG. HDR
- * and SDR inputs must be the same resolution and color space.
+ * and SDR inputs must be the same resolution.
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param compressed_jpeg_image compressed 8-bit JPEG image
+ * @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image
* @return NO_ERROR if encoding succeeds, error code if error occurs.
*/
status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest);
/*
* Decompress JPEGR image.
*
+ * The output JPEGR image is in RGBA_1010102 data format if decoding to HDR.
* @param compressed_jpegr_image compressed JPEGR image
* @param dest destination of the uncompressed JPEGR image
- * @param exif destination of the decoded EXIF metadata. Default value is nullptr where EXIF
- * metadata will not be decoded.
- * @param request_sdr flag that request SDR output, default to false (request HDR output). If
- * set to true, decoder will only decode the primary image which is SDR.
- * Setting of request_sdr and input source (HDR or SDR) can be found in
- * the table below:
+ * @param exif destination of the decoded EXIF metadata.
+ * @param request_sdr flag that request SDR output. If set to true, decoder will only decode
+ * the primary image which is SDR. Setting of request_sdr and input source
+ * (HDR or SDR) can be found in the table below:
* | input source | request_sdr | output of decoding |
* | HDR | true | SDR |
* | HDR | false | HDR |
@@ -145,8 +198,8 @@
*/
status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
jr_uncompressed_ptr dest,
- jr_exif_ptr exif = nullptr,
- bool request_sdr = false);
+ jr_exif_ptr exif,
+ bool request_sdr);
private:
/*
* This method is called in the decoding pipeline. It will decode the recovery map.
@@ -176,26 +229,32 @@
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param dest recovery map; caller responsible for memory of data
- * @param hdr_ratio HDR ratio will be updated in this method
+ * @param metadata metadata provides the transfer function for the HDR
+ * image; range_scaling_factor and hdr10 FALL and CLL will
+ * be updated.
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr dest,
- float &hdr_ratio);
+ jr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest);
/*
* This method is called in the decoding pipeline. It will take the uncompressed (decoded)
- * 8-bit yuv image and the uncompressed (decoded) recovery map as input, and calculate the
- * 10-bit recovered image (in p010 color format).
+ * 8-bit yuv image, the uncompressed (decoded) recovery map, and extracted JPEG/R metadata as
+ * input, and calculate the 10-bit recovered image. The recovered output image is the same
+ * color gamut as the SDR image, with the transfer function specified in the JPEG/R metadata,
+ * and is in RGBA1010102 data format.
*
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
* @param uncompressed_recovery_map uncompressed recovery map
+ * @param metadata JPEG/R metadata extracted from XMP.
* @param dest reconstructed HDR image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
status_t applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_metadata_ptr metadata,
jr_uncompressed_ptr dest);
/*
@@ -204,9 +263,12 @@
*
* @param compressed_jpegr_image compressed JPEGR image
* @param dest destination of compressed recovery map
+ * @param metadata destination of the recovery map metadata
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, jr_compressed_ptr dest);
+ status_t extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+ jr_compressed_ptr dest,
+ jr_metadata_ptr metadata);
/*
* This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image
@@ -215,13 +277,13 @@
*
* @param compressed_jpeg_image compressed 8-bit JPEG image
* @param compress_recovery_map compressed recover map
- * @param hdr_ratio HDR ratio
+ * @param metadata JPEG/R metadata to encode in XMP of the jpeg
* @param dest compressed JPEGR image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
status_t appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
jr_compressed_ptr compressed_recovery_map,
- float hdr_ratio,
+ jr_metadata_ptr metadata,
jr_compressed_ptr dest);
/*
@@ -229,7 +291,7 @@
*
* below is an example of the XMP metadata that this function generates where
* secondary_image_length = 1000
- * hdr_ratio = 1.25
+ * range_scaling_factor = 1.25
*
* <x:xmpmeta
* xmlns:x="adobe:ns:meta/"
@@ -239,7 +301,7 @@
* <rdf:Description
* xmlns:GContainer="http://ns.google.com/photos/1.0/container/">
* <GContainer:Version>1</GContainer:Version>
- * <GContainer:HdrRatio>1.25</GContainer:HdrRatio>
+ * <GContainer:RangeScalingFactor>1.25</GContainer:RangeScalingFactor>
* <GContainer:Directory>
* <rdf:Seq>
* <rdf:li>
@@ -260,10 +322,10 @@
* </x:xmpmeta>
*
* @param secondary_image_length length of secondary image
- * @param hdr_ratio hdr ratio
+ * @param metadata JPEG/R metadata to encode as XMP
* @return XMP metadata in type of string
*/
- std::string generateXmp(int secondary_image_length, float hdr_ratio);
+ std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
};
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
index e76bc20..fe7a651 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -43,6 +43,9 @@
};
};
+typedef Color (*ColorTransformFn)(Color);
+typedef float (*ColorCalculationFn)(Color);
+
inline Color operator+=(Color& lhs, const Color& rhs) {
lhs.r += rhs.r;
lhs.g += rhs.g;
@@ -138,7 +141,10 @@
////////////////////////////////////////////////////////////////////////////////
// Display-P3 transformations
-
+/*
+ * Calculated the luminance of a linear RGB P3 pixel, according to EG 432-1.
+ */
+float p3Luminance(Color e);
////////////////////////////////////////////////////////////////////////////////
@@ -169,10 +175,43 @@
*/
Color hlgInvOetf(Color e_gamma);
+/*
+ * Convert from scene luminance in nits to PQ.
+ */
+Color pqOetf(Color e);
+
+/*
+ * Convert from PQ to scene luminance in nits.
+ */
+Color pqInvOetf(Color e_gamma);
+
+
////////////////////////////////////////////////////////////////////////////////
// Color space conversions
+/*
+ * Convert between color spaces with linear RGB data, according to ITU-R BT.2407 and EG 432-1.
+ *
+ * All conversions are derived from multiplying the matrix for XYZ to output RGB color gamut by the
+ * matrix for input RGB color gamut to XYZ. The matrix for converting from XYZ to an RGB gamut is
+ * always the inverse of the RGB gamut to XYZ matrix.
+ */
+Color bt709ToP3(Color e);
+Color bt709ToBt2100(Color e);
+Color p3ToBt709(Color e);
+Color p3ToBt2100(Color e);
+Color bt2100ToBt709(Color e);
+Color bt2100ToP3(Color e);
+/*
+ * Identity conversion.
+ */
+inline Color identityConversion(Color e) { return e; }
+
+/*
+ * Get the conversion to apply to the HDR image for recovery map generation
+ */
+ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut);
////////////////////////////////////////////////////////////////////////////////
@@ -220,6 +259,13 @@
*/
Color sampleP010(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
+/*
+ * Convert from Color to RGBA1010102.
+ *
+ * Alpha always set to 1.0.
+ */
+uint32_t colorToRgba1010102(Color e_gamma);
+
} // namespace android::recoverymap
#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index 86eb8a8..64021b7 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-// TODO: need to clean up handling around hdr_ratio and passing it around
-// TODO: need to handle color space information; currently we assume everything
-// is srgb in.
-// TODO: handle PQ encode/decode (currently only HLG)
-
#include <jpegrecoverymap/recoverymap.h>
#include <jpegrecoverymap/jpegencoder.h>
#include <jpegrecoverymap/jpegdecoder.h>
@@ -43,9 +38,21 @@
} \
}
+// The current JPEGR version that we encode to
+static const uint32_t kJpegrVersion = 1;
+
// Map is quarter res / sixteenth size
static const size_t kMapDimensionScaleFactor = 4;
+// TODO: fill in st2086 metadata
+static const st2086_metadata kSt2086Metadata = {
+ {0.0f, 0.0f},
+ {0.0f, 0.0f},
+ {0.0f, 0.0f},
+ {0.0f, 0.0f},
+ 0,
+ 1.0f,
+};
/*
* Helper function used for generating XMP metadata.
@@ -81,6 +88,7 @@
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_uncompressed_ptr uncompressed_yuv_420_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest,
int quality,
jr_exif_ptr /* exif */) {
@@ -99,10 +107,16 @@
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
jpegr_uncompressed_struct map;
- float hdr_ratio = 0.0f;
JPEGR_CHECK(generateRecoveryMap(
- uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio));
+ uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(map.data));
@@ -113,7 +127,7 @@
JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
JpegEncoder jpeg_encoder;
- // TODO: ICC data - need color space information
+ // TODO: determine ICC data based on color gamut information
if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data,
uncompressed_yuv_420_image->width,
uncompressed_yuv_420_image->height, quality, nullptr, 0)) {
@@ -123,7 +137,7 @@
jpeg.data = jpeg_encoder.getCompressedImagePtr();
jpeg.length = jpeg_encoder.getCompressedImageSize();
- JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, hdr_ratio, dest));
+ JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &metadata, dest));
return NO_ERROR;
}
@@ -131,6 +145,7 @@
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest) {
if (uncompressed_p010_image == nullptr
|| uncompressed_yuv_420_image == nullptr
@@ -144,10 +159,16 @@
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
jpegr_uncompressed_struct map;
- float hdr_ratio = 0.0f;
JPEGR_CHECK(generateRecoveryMap(
- uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio));
+ uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(map.data));
@@ -157,13 +178,14 @@
compressed_map.data = compressed_map_data.get();
JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
- JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, hdr_ratio, dest));
+ JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, &metadata, dest));
return NO_ERROR;
}
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_compressed_ptr compressed_jpeg_image,
+ jpegr_transfer_function hdr_tf,
jr_compressed_ptr dest) {
if (uncompressed_p010_image == nullptr
|| compressed_jpeg_image == nullptr
@@ -179,16 +201,23 @@
uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
+ uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
|| uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
jpegr_uncompressed_struct map;
- float hdr_ratio = 0.0f;
JPEGR_CHECK(generateRecoveryMap(
- &uncompressed_yuv_420_image, uncompressed_p010_image, &map, hdr_ratio));
+ &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(map.data));
@@ -198,7 +227,7 @@
compressed_map.data = compressed_map_data.get();
JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
- JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, hdr_ratio, dest));
+ JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, &metadata, dest));
return NO_ERROR;
}
@@ -212,7 +241,8 @@
}
jpegr_compressed_struct compressed_map;
- JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
+ jpegr_metadata metadata;
+ JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map, &metadata));
jpegr_uncompressed_struct map;
JPEGR_CHECK(decompressRecoveryMap(&compressed_map, &map));
@@ -227,7 +257,7 @@
uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
- JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, dest));
+ JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
return NO_ERROR;
}
@@ -257,7 +287,7 @@
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- // TODO: should we have ICC data?
+ // TODO: should we have ICC data for the map?
JpegEncoder jpeg_encoder;
if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data, uncompressed_recovery_map->width,
uncompressed_recovery_map->height, 85, nullptr, 0,
@@ -271,16 +301,18 @@
memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize());
dest->length = jpeg_encoder.getCompressedImageSize();
+ dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
return NO_ERROR;
}
status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_p010_image,
- jr_uncompressed_ptr dest,
- float &hdr_ratio) {
+ jr_metadata_ptr metadata,
+ jr_uncompressed_ptr dest) {
if (uncompressed_yuv_420_image == nullptr
|| uncompressed_p010_image == nullptr
+ || metadata == nullptr
|| dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -290,6 +322,11 @@
return ERROR_JPEGR_RESOLUTION_MISMATCH;
}
+ if (uncompressed_yuv_420_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED
+ || uncompressed_p010_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED) {
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+
size_t image_width = uncompressed_yuv_420_image->width;
size_t image_height = uncompressed_yuv_420_image->height;
size_t map_width = image_width / kMapDimensionScaleFactor;
@@ -297,25 +334,63 @@
dest->width = map_width;
dest->height = map_height;
+ dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
dest->data = new uint8_t[map_width * map_height];
std::unique_ptr<uint8_t[]> map_data;
map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
+ ColorTransformFn hdrInvOetf = nullptr;
+ switch (metadata->transferFunction) {
+ case JPEGR_TF_HLG:
+ hdrInvOetf = hlgInvOetf;
+ break;
+ case JPEGR_TF_PQ:
+ hdrInvOetf = pqInvOetf;
+ break;
+ }
+
+ ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
+ uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
+
+ ColorCalculationFn luminanceFn = nullptr;
+ switch (uncompressed_yuv_420_image->colorGamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ luminanceFn = srgbLuminance;
+ break;
+ case JPEGR_COLORGAMUT_P3:
+ luminanceFn = p3Luminance;
+ break;
+ case JPEGR_COLORGAMUT_BT2100:
+ luminanceFn = bt2100Luminance;
+ break;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ // Should be impossible to hit after input validation.
+ return ERROR_JPEGR_INVALID_COLORGAMUT;
+ }
+
float hdr_y_nits_max = 0.0f;
+ double hdr_y_nits_avg = 0.0f;
for (size_t y = 0; y < image_height; ++y) {
for (size_t x = 0; x < image_width; ++x) {
Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y);
Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
- Color hdr_rgb = hlgInvOetf(hdr_rgb_gamma);
- float hdr_y_nits = bt2100Luminance(hdr_rgb);
+ Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
+ hdr_rgb = hdrGamutConversionFn(hdr_rgb);
+ float hdr_y_nits = luminanceFn(hdr_rgb);
+ hdr_y_nits_avg += hdr_y_nits;
if (hdr_y_nits > hdr_y_nits_max) {
hdr_y_nits_max = hdr_y_nits;
}
}
}
+ hdr_y_nits_avg /= image_width * image_height;
- hdr_ratio = hdr_y_nits_max / kSdrWhiteNits;
+ metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits;
+ if (metadata->transferFunction == JPEGR_TF_PQ) {
+ metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg;
+ metadata->hdr10Metadata.maxCLL = hdr_y_nits_max;
+ }
for (size_t y = 0; y < map_height; ++y) {
for (size_t x = 0; x < map_width; ++x) {
@@ -323,16 +398,17 @@
kMapDimensionScaleFactor, x, y);
Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
- float sdr_y_nits = srgbLuminance(sdr_rgb);
+ float sdr_y_nits = luminanceFn(sdr_rgb);
Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
- Color hdr_rgb = hlgInvOetf(hdr_rgb_gamma);
- float hdr_y_nits = bt2100Luminance(hdr_rgb);
+ Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
+ hdr_rgb = hdrGamutConversionFn(hdr_rgb);
+ float hdr_y_nits = luminanceFn(hdr_rgb);
size_t pixel_idx = x + y * map_width;
reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
- encodeRecovery(sdr_y_nits, hdr_y_nits, hdr_ratio);
+ encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor);
}
}
@@ -342,17 +418,15 @@
status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_uncompressed_ptr uncompressed_recovery_map,
+ jr_metadata_ptr metadata,
jr_uncompressed_ptr dest) {
if (uncompressed_yuv_420_image == nullptr
|| uncompressed_recovery_map == nullptr
+ || metadata == nullptr
|| dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- // TODO: need to get this from the XMP; should probably be a function
- // parameter
- float hdr_ratio = 4.0f;
-
size_t width = uncompressed_yuv_420_image->width;
size_t height = uncompressed_yuv_420_image->height;
@@ -360,26 +434,31 @@
dest->height = height;
size_t pixel_count = width * height;
+ ColorTransformFn hdrOetf = nullptr;
+ switch (metadata->transferFunction) {
+ case JPEGR_TF_HLG:
+ hdrOetf = hlgOetf;
+ break;
+ case JPEGR_TF_PQ:
+ hdrOetf = pqOetf;
+ break;
+ }
+
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
- size_t pixel_y_idx = x + y * width;
+ Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
+ Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr);
+ Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr);
- size_t pixel_uv_idx = x / 2 + (y / 2) * (width / 2);
-
- Color ypuv_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
- Color rgbp_sdr = srgbYuvToRgb(ypuv_sdr);
- Color rgb_sdr = srgbInvOetf(rgbp_sdr);
-
+ // TODO: determine map scaling factor based on actual map dims
float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y);
- Color rgb_hdr = applyRecovery(rgb_sdr, recovery, hdr_ratio);
+ Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata->rangeScalingFactor);
- Color rgbp_hdr = hlgOetf(rgb_hdr);
- // TODO: actually just leave in RGB and convert to RGBA1010102 instead.
- Color ypuv_hdr = srgbRgbToYuv(rgbp_hdr);
+ Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
+ uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
- reinterpret_cast<uint16_t*>(dest->data)[pixel_y_idx] = ypuv_hdr.r;
- reinterpret_cast<uint16_t*>(dest->data)[pixel_count + pixel_uv_idx] = ypuv_hdr.g;
- reinterpret_cast<uint16_t*>(dest->data)[pixel_count + pixel_uv_idx + 1] = ypuv_hdr.b;
+ size_t pixel_idx = x + y * width;
+ reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
}
}
@@ -387,8 +466,9 @@
}
status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
- jr_compressed_ptr dest) {
- if (compressed_jpegr_image == nullptr || dest == nullptr) {
+ jr_compressed_ptr dest,
+ jr_metadata_ptr metadata) {
+ if (compressed_jpegr_image == nullptr || dest == nullptr || metadata == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
@@ -398,15 +478,16 @@
status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
jr_compressed_ptr compressed_recovery_map,
- float hdr_ratio,
+ jr_metadata_ptr metadata,
jr_compressed_ptr dest) {
if (compressed_jpeg_image == nullptr
|| compressed_recovery_map == nullptr
+ || metadata == nullptr
|| dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
}
- string xmp = generateXmp(compressed_recovery_map->length, hdr_ratio);
+ string xmp = generateXmp(compressed_recovery_map->length, *metadata);
string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
// 2 bytes: APP1 sign (ff e1)
@@ -442,7 +523,7 @@
return NO_ERROR;
}
-string RecoveryMap::generateXmp(int secondary_image_length, float hdr_ratio) {
+string RecoveryMap::generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
const string kContainerPrefix = "GContainer";
const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
const string kItemPrefix = "Item";
@@ -455,7 +536,6 @@
const string kPrimary = "Primary";
const string kSemantic = "Semantic";
const string kVersion = "Version";
- const int kVersionValue = 1;
const string kConDir = Name(kContainerPrefix, kDirectory);
const string kContainerItem = Name(kContainerPrefix, kItem);
@@ -475,8 +555,11 @@
writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
writer.StartWritingElement("rdf:Description");
writer.WriteXmlns(kContainerPrefix, kContainerUri);
- writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), kVersionValue);
- writer.WriteElementAndContent(Name(kContainerPrefix, "HdrRatio"), hdr_ratio);
+ writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), metadata.version);
+ writer.WriteElementAndContent(Name(kContainerPrefix, "rangeScalingFactor"),
+ metadata.rangeScalingFactor);
+ // TODO: determine structure for hdr10 metadata
+ // TODO: write rest of metadata
writer.StartWritingElements(kConDirSeq);
size_t item_depth = writer.StartWritingElements(kLiItem);
writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary);
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 0d3319f..4541f9b 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -64,6 +64,11 @@
////////////////////////////////////////////////////////////////////////////////
// Display-P3 transformations
+static const float kP3R = 0.22897f, kP3G = 0.69174f, kP3B = 0.07929f;
+
+float p3Luminance(Color e) {
+ return kP3R * e.r + kP3G * e.g + kP3B * e.b;
+}
////////////////////////////////////////////////////////////////////////////////
@@ -128,7 +133,7 @@
return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}};
}
-float hlgInvOetf(float e_gamma) {
+static float hlgInvOetf(float e_gamma) {
if (e_gamma <= 0.5f) {
return pow(e_gamma, 2.0f) / 3.0f;
} else {
@@ -142,10 +147,118 @@
hlgInvOetf(e_gamma.b) }}};
}
+static const float kPqM1 = 2610.0f / 16384.0f, kPqM2 = 2523.0f / 4096.0f * 128.0f;
+static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f,
+ kPqC3 = 2392.0f / 4096.0f * 32.0f;
+
+static float pqOetf(float e) {
+ if (e < 0.0f) e = 0.0f;
+ return pow((kPqC1 + kPqC2 * pow(e / 10000.0f, kPqM1)) / (1 + kPqC3 * pow(e / 10000.0f, kPqM1)),
+ kPqM2);
+}
+
+Color pqOetf(Color e) {
+ return {{{ pqOetf(e.r), pqOetf(e.g), pqOetf(e.b) }}};
+}
+
+static float pqInvOetf(float e_gamma) {
+ static const float kPqInvOetfCoef = log2(-(pow(kPqM1, 1.0f / kPqM2) - kPqC1)
+ / (kPqC3 * pow(kPqM1, 1.0f / kPqM2) - kPqC2));
+ return kPqInvOetfCoef / log2(e_gamma * 10000.0f);
+}
+
+Color pqInvOetf(Color e_gamma) {
+ return {{{ pqInvOetf(e_gamma.r),
+ pqInvOetf(e_gamma.g),
+ pqInvOetf(e_gamma.b) }}};
+}
+
////////////////////////////////////////////////////////////////////////////////
// Color conversions
+Color bt709ToP3(Color e) {
+ return {{{ 0.82254f * e.r + 0.17755f * e.g + 0.00006f * e.b,
+ 0.03312f * e.r + 0.96684f * e.g + -0.00001f * e.b,
+ 0.01706f * e.r + 0.07240f * e.g + 0.91049f * e.b }}};
+}
+
+Color bt709ToBt2100(Color e) {
+ return {{{ 0.62740f * e.r + 0.32930f * e.g + 0.04332f * e.b,
+ 0.06904f * e.r + 0.91958f * e.g + 0.01138f * e.b,
+ 0.01636f * e.r + 0.08799f * e.g + 0.89555f * e.b }}};
+}
+
+Color p3ToBt709(Color e) {
+ return {{{ 1.22482f * e.r + -0.22490f * e.g + -0.00007f * e.b,
+ -0.04196f * e.r + 1.04199f * e.g + 0.00001f * e.b,
+ -0.01961f * e.r + -0.07865f * e.g + 1.09831f * e.b }}};
+}
+
+Color p3ToBt2100(Color e) {
+ return {{{ 0.75378f * e.r + 0.19862f * e.g + 0.04754f * e.b,
+ 0.04576f * e.r + 0.94177f * e.g + 0.01250f * e.b,
+ -0.00121f * e.r + 0.01757f * e.g + 0.98359f * e.b }}};
+}
+
+Color bt2100ToBt709(Color e) {
+ return {{{ 1.66045f * e.r + -0.58764f * e.g + -0.07286f * e.b,
+ -0.12445f * e.r + 1.13282f * e.g + -0.00837f * e.b,
+ -0.01811f * e.r + -0.10057f * e.g + 1.11878f * e.b }}};
+}
+
+Color bt2100ToP3(Color e) {
+ return {{{ 1.34369f * e.r + -0.28223f * e.g + -0.06135f * e.b,
+ -0.06533f * e.r + 1.07580f * e.g + -0.01051f * e.b,
+ 0.00283f * e.r + -0.01957f * e.g + 1.01679f * e.b
+ }}};
+}
+
+// TODO: confirm we always want to convert like this before calculating
+// luminance.
+ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gamut hdr_gamut) {
+ switch (sdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ switch (hdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ return identityConversion;
+ case JPEGR_COLORGAMUT_P3:
+ return p3ToBt709;
+ case JPEGR_COLORGAMUT_BT2100:
+ return bt2100ToBt709;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+ break;
+ case JPEGR_COLORGAMUT_P3:
+ switch (hdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ return bt709ToP3;
+ case JPEGR_COLORGAMUT_P3:
+ return identityConversion;
+ case JPEGR_COLORGAMUT_BT2100:
+ return bt2100ToP3;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+ break;
+ case JPEGR_COLORGAMUT_BT2100:
+ switch (hdr_gamut) {
+ case JPEGR_COLORGAMUT_BT709:
+ return bt709ToBt2100;
+ case JPEGR_COLORGAMUT_P3:
+ return p3ToBt2100;
+ case JPEGR_COLORGAMUT_BT2100:
+ return identityConversion;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+ break;
+ case JPEGR_COLORGAMUT_UNSPECIFIED:
+ return nullptr;
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// Recovery map calculations
@@ -257,4 +370,11 @@
return samplePixels(image, map_scale_factor, x, y, getP010Pixel);
}
+uint32_t colorToRgba1010102(Color e_gamma) {
+ return (0x3ff & static_cast<uint32_t>(e_gamma.r * 1023.0f))
+ | ((0x3ff & static_cast<uint32_t>(e_gamma.g * 1023.0f)) << 10)
+ | ((0x3ff & static_cast<uint32_t>(e_gamma.b * 1023.0f)) << 20)
+ | (0x3 << 30); // Set alpha to 1.0
+}
+
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
index 226056b..b3cd37e 100644
--- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
@@ -37,9 +37,11 @@
TEST_F(RecoveryMapTest, build) {
// Force all of the recovery map lib to be linked by calling all public functions.
RecoveryMap recovery_map;
- recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, 0, nullptr);
- recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, nullptr);
- recovery_map.encodeJPEGR(nullptr, nullptr, nullptr);
+ recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
+ nullptr, 0, nullptr);
+ recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
+ nullptr);
+ recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false);
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8a3e784..d53f0b1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1123,7 +1123,7 @@
// We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
// the same reason we are allowing touch boost for those layers. See
- // RefreshRateSelector::rankRefreshRates for details.
+ // RefreshRateSelector::rankFrameRates for details.
const auto layerVotedWithDefaultCompatibility =
frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Default;
const auto layerVotedWithNoVote = frameRate.type == FrameRateCompatibility::NoVote;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 8d4ea05..fd1a733 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -24,6 +24,7 @@
#include <chrono>
#include <cmath>
#include <deque>
+#include <map>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -31,6 +32,7 @@
#include <ftl/fake_guard.h>
#include <ftl/match.h>
#include <ftl/unit.h>
+#include <scheduler/FrameRateMode.h>
#include <utils/Trace.h>
#include "../SurfaceFlingerProperties.h"
@@ -43,7 +45,7 @@
namespace {
struct RefreshRateScore {
- DisplayModeIterator modeIt;
+ FrameRateMode frameRateMode;
float overallScore;
struct {
float modeBelowThreshold;
@@ -77,19 +79,11 @@
return knownFrameRates;
}
-// The Filter is a `bool(const DisplayMode&)` predicate.
-template <typename Filter>
-std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Filter&& filter) {
+std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes) {
std::vector<DisplayModeIterator> sortedModes;
sortedModes.reserve(modes.size());
-
for (auto it = modes.begin(); it != modes.end(); ++it) {
- const auto& [id, mode] = *it;
-
- if (filter(*mode)) {
- ALOGV("%s: including mode %d", __func__, id.value());
- sortedModes.push_back(it);
- }
+ sortedModes.push_back(it);
}
std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) {
@@ -106,6 +100,21 @@
return sortedModes;
}
+std::pair<unsigned, unsigned> divisorRange(Fps fps, FpsRange range,
+ RefreshRateSelector::Config::FrameRateOverride config) {
+ if (config != RefreshRateSelector::Config::FrameRateOverride::Enabled) {
+ return {1, 1};
+ }
+
+ using fps_approx_ops::operator/;
+ const auto start = std::max(1u, fps / range.max - 1);
+ const auto end = fps /
+ std::max(range.min, RefreshRateSelector::kMinSupportedFrameRate,
+ fps_approx_ops::operator<);
+
+ return {start, end};
+}
+
bool shouldEnableFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) {
for (const auto it1 : sortedModes) {
const auto& mode1 = it1->second;
@@ -136,27 +145,109 @@
} // namespace
+auto RefreshRateSelector::createFrameRateModes(
+ std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange& renderRange) const
+ -> std::vector<FrameRateMode> {
+ struct Key {
+ Fps fps;
+ int32_t group;
+ };
+
+ struct KeyLess {
+ bool operator()(const Key& a, const Key& b) const {
+ using namespace fps_approx_ops;
+ if (a.fps != b.fps) {
+ return a.fps < b.fps;
+ }
+
+ // For the same fps the order doesn't really matter, but we still
+ // want the behaviour of a strictly less operator.
+ // We use the group id as the secondary ordering for that.
+ return a.group < b.group;
+ }
+ };
+
+ std::map<Key, DisplayModeIterator, KeyLess> ratesMap;
+ for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) {
+ const auto& [id, mode] = *it;
+
+ if (!filterModes(*mode)) {
+ continue;
+ }
+ const auto [start, end] =
+ divisorRange(mode->getFps(), renderRange, mConfig.enableFrameRateOverride);
+ for (auto divisor = start; divisor <= end; divisor++) {
+ const auto fps = mode->getFps() / divisor;
+ using fps_approx_ops::operator<;
+ if (fps < kMinSupportedFrameRate) {
+ break;
+ }
+
+ if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Enabled &&
+ !renderRange.includes(fps)) {
+ continue;
+ }
+
+ if (mConfig.enableFrameRateOverride ==
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates &&
+ !isNativeRefreshRate(fps)) {
+ continue;
+ }
+
+ const auto [existingIter, emplaceHappened] =
+ ratesMap.try_emplace(Key{fps, mode->getGroup()}, it);
+ if (emplaceHappened) {
+ ALOGV("%s: including %s (%s)", __func__, to_string(fps).c_str(),
+ to_string(mode->getFps()).c_str());
+ } else {
+ // We might need to update the map as we found a lower refresh rate
+ if (isStrictlyLess(mode->getFps(), existingIter->second->second->getFps())) {
+ existingIter->second = it;
+ ALOGV("%s: changing %s (%s)", __func__, to_string(fps).c_str(),
+ to_string(mode->getFps()).c_str());
+ }
+ }
+ }
+ }
+
+ std::vector<FrameRateMode> frameRateModes;
+ frameRateModes.reserve(ratesMap.size());
+ for (const auto& [key, mode] : ratesMap) {
+ frameRateModes.emplace_back(FrameRateMode{key.fps, mode->second});
+ }
+
+ // We always want that the lowest frame rate will be corresponding to the
+ // lowest mode for power saving.
+ const auto lowestRefreshRateIt =
+ std::min_element(frameRateModes.begin(), frameRateModes.end(),
+ [](const FrameRateMode& lhs, const FrameRateMode& rhs) {
+ return isStrictlyLess(lhs.modePtr->getFps(),
+ rhs.modePtr->getFps());
+ });
+ frameRateModes.erase(frameRateModes.begin(), lowestRefreshRateIt);
+
+ return frameRateModes;
+}
+
struct RefreshRateSelector::RefreshRateScoreComparator {
bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const {
- const auto& [modeIt, overallScore, _] = lhs;
+ const auto& [frameRateMode, overallScore, _] = lhs;
- std::string name = to_string(modeIt->second->getFps());
+ std::string name = to_string(frameRateMode);
+
ALOGV("%s sorting scores %.2f", name.c_str(), overallScore);
-
ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
- if (!ScoredRefreshRate::scoresEqual(overallScore, rhs.overallScore)) {
+ if (!ScoredFrameRate::scoresEqual(overallScore, rhs.overallScore)) {
return overallScore > rhs.overallScore;
}
- // If overallScore tie we will pick the higher refresh rate if
- // high refresh rate is the priority else the lower refresh rate.
if (refreshRateOrder == RefreshRateOrder::Descending) {
using fps_approx_ops::operator>;
- return modeIt->second->getFps() > rhs.modeIt->second->getFps();
+ return frameRateMode.fps > rhs.frameRateMode.fps;
} else {
using fps_approx_ops::operator<;
- return modeIt->second->getFps() < rhs.modeIt->second->getFps();
+ return frameRateMode.fps < rhs.frameRateMode.fps;
}
}
@@ -210,7 +301,15 @@
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
layer.vote == LayerVoteType::Heuristic) {
- if (isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
+ const float multiplier = refreshRate.getValue() / layer.desiredRefreshRate.getValue();
+
+ // We only want to score this layer as a fractional pair if the content is not
+ // significantly faster than the display rate, at it would cause a significant frame drop.
+ // It is more appropriate to choose a higher display rate even if
+ // a pull-down will be required.
+ constexpr float kMinMultiplier = 0.25f;
+ if (multiplier >= kMinMultiplier &&
+ isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
return kScoreForFractionalPairs;
}
@@ -245,9 +344,9 @@
return 0;
}
-float RefreshRateSelector::calculateRefreshRateScoreForFps(Fps refreshRate) const {
- const float ratio =
- refreshRate.getValue() / mAppRequestRefreshRates.back()->second->getFps().getValue();
+float RefreshRateSelector::calculateDistanceScoreFromMax(Fps refreshRate) const {
+ const auto& maxFps = mAppRequestFrameRates.back().fps;
+ const float ratio = refreshRate.getValue() / maxFps.getValue();
// Use ratio^2 to get a lower score the more we get further from peak
return ratio * ratio;
}
@@ -260,12 +359,12 @@
// If the layer wants Max, give higher score to the higher refresh rate
if (layer.vote == LayerVoteType::Max) {
- return calculateRefreshRateScoreForFps(refreshRate);
+ return calculateDistanceScoreFromMax(refreshRate);
}
if (layer.vote == LayerVoteType::ExplicitExact) {
const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate);
- if (supportsFrameRateOverrideByContent()) {
+ if (supportsAppFrameRateOverrideByContent()) {
// Since we support frame rate override, allow refresh rates which are
// multiples of the layer's request, as those apps would be throttled
// down to run at the desired refresh rate.
@@ -289,33 +388,33 @@
kNonExactMatchingPenalty;
}
-auto RefreshRateSelector::getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const -> RankedRefreshRates {
+auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const -> RankedFrameRates {
std::lock_guard lock(mLock);
- if (mGetRankedRefreshRatesCache &&
- mGetRankedRefreshRatesCache->arguments == std::make_pair(layers, signals)) {
- return mGetRankedRefreshRatesCache->result;
+ if (mGetRankedFrameRatesCache &&
+ mGetRankedFrameRatesCache->arguments == std::make_pair(layers, signals)) {
+ return mGetRankedFrameRatesCache->result;
}
- const auto result = getRankedRefreshRatesLocked(layers, signals);
- mGetRankedRefreshRatesCache = GetRankedRefreshRatesCache{{layers, signals}, result};
+ const auto result = getRankedFrameRatesLocked(layers, signals);
+ mGetRankedFrameRatesCache = GetRankedFrameRatesCache{{layers, signals}, result};
return result;
}
-auto RefreshRateSelector::getRankedRefreshRatesLocked(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const
- -> RankedRefreshRates {
+auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const
+ -> RankedFrameRates {
using namespace fps_approx_ops;
ATRACE_CALL();
ALOGV("%s: %zu layers", __func__, layers.size());
const auto& activeMode = *getActiveModeItLocked()->second;
- // Keep the display at max refresh rate for the duration of powering on the display.
+ // Keep the display at max frame rate for the duration of powering on the display.
if (signals.powerOnImminent) {
ALOGV("Power On Imminent");
- return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Descending),
+ return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending),
GlobalSignals{.powerOnImminent = true}};
}
@@ -375,7 +474,7 @@
// selected a refresh rate to see if we should apply touch boost.
if (signals.touch && !hasExplicitVoteLayers) {
ALOGV("Touch Boost");
- return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending),
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending),
GlobalSignals{.touch = true}};
}
@@ -387,27 +486,27 @@
if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
ALOGV("Idle");
- return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Ascending),
+ return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending),
GlobalSignals{.idle = true}};
}
if (layers.empty() || noVoteLayers == layers.size()) {
ALOGV("No layers with votes");
- return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
}
// Only if all layers want Min we should return Min
if (noVoteLayers + minVoteLayers == layers.size()) {
ALOGV("All layers Min");
- return {rankRefreshRates(activeMode.getGroup(), RefreshRateOrder::Ascending), kNoSignals};
+ return {rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending), kNoSignals};
}
// Find the best refresh rate based on score
std::vector<RefreshRateScore> scores;
- scores.reserve(mAppRequestRefreshRates.size());
+ scores.reserve(mAppRequestFrameRates.size());
- for (const DisplayModeIterator modeIt : mAppRequestRefreshRates) {
- scores.emplace_back(RefreshRateScore{modeIt, 0.0f});
+ for (const FrameRateMode& it : mAppRequestFrameRates) {
+ scores.emplace_back(RefreshRateScore{it, 0.0f});
}
for (const auto& layer : layers) {
@@ -420,13 +519,13 @@
const auto weight = layer.weight;
- for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
- const auto& [id, mode] = *modeIt;
- const bool isSeamlessSwitch = mode->getGroup() == activeMode.getGroup();
+ for (auto& [mode, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+ const auto& [fps, modePtr] = mode;
+ const bool isSeamlessSwitch = modePtr->getGroup() == activeMode.getGroup();
if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
- formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
+ formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
to_string(activeMode).c_str());
continue;
}
@@ -435,7 +534,7 @@
!layer.focused) {
ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
" Current mode = %s",
- formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
+ formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
to_string(activeMode).c_str());
continue;
}
@@ -445,14 +544,14 @@
// mode group otherwise. In second case, if the current mode group is different
// from the default, this means a layer with seamlessness=SeamedAndSeamless has just
// disappeared.
- const bool isInPolicyForDefault = mode->getGroup() == anchorGroup;
+ const bool isInPolicyForDefault = modePtr->getGroup() == anchorGroup;
if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
- to_string(*mode).c_str(), to_string(activeMode).c_str());
+ to_string(*modePtr).c_str(), to_string(activeMode).c_str());
continue;
}
- const bool inPrimaryRange = policy->primaryRanges.physical.includes(mode->getFps());
+ const bool inPrimaryRange = policy->primaryRanges.physical.includes(modePtr->getFps());
if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
!(layer.focused &&
(layer.vote == LayerVoteType::ExplicitDefault ||
@@ -462,8 +561,7 @@
continue;
}
- const float layerScore =
- calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch);
+ const float layerScore = calculateLayerScoreLocked(layer, fps, isSeamlessSwitch);
const float weightedLayerScore = weight * layerScore;
// Layer with fixed source has a special consideration which depends on the
@@ -491,21 +589,21 @@
Fps::fromValue(mConfig.frameRateMultipleThreshold / 2);
if (fixedSourceLayer && layerBelowThreshold) {
const bool modeAboveThreshold =
- mode->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
+ modePtr->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
if (modeAboveThreshold) {
- ALOGV("%s gives %s fixed source (above threshold) score of %.4f",
- formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
- layerScore);
+ ALOGV("%s gives %s (%s) fixed source (above threshold) score of %.4f",
+ formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
+ to_string(modePtr->getFps()).c_str(), layerScore);
fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore;
} else {
- ALOGV("%s gives %s fixed source (below threshold) score of %.4f",
- formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
- layerScore);
+ ALOGV("%s gives %s (%s) fixed source (below threshold) score of %.4f",
+ formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
+ to_string(modePtr->getFps()).c_str(), layerScore);
fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore;
}
} else {
- ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
- to_string(mode->getFps()).c_str(), layerScore);
+ ALOGV("%s gives %s (%s) score of %.4f", formatLayerInfo(layer, weight).c_str(),
+ to_string(fps).c_str(), to_string(modePtr->getFps()).c_str(), layerScore);
overallScore += weightedLayerScore;
}
}
@@ -524,25 +622,26 @@
const auto maxScoreIt =
std::max_element(scores.begin(), scores.end(),
[](RefreshRateScore max, RefreshRateScore current) {
- const auto& [modeIt, overallScore, _] = current;
- return overallScore > max.overallScore;
+ return current.overallScore > max.overallScore;
});
- ALOGV("%s is the best refresh rate without fixed source layers. It is %s the threshold for "
+ ALOGV("%s (%s) is the best refresh rate without fixed source layers. It is %s the "
+ "threshold for "
"refresh rate multiples",
- to_string(maxScoreIt->modeIt->second->getFps()).c_str(),
+ to_string(maxScoreIt->frameRateMode.fps).c_str(),
+ to_string(maxScoreIt->frameRateMode.modePtr->getFps()).c_str(),
maxScoreAboveThreshold ? "above" : "below");
- return maxScoreIt->modeIt->second->getFps() >=
+ return maxScoreIt->frameRateMode.modePtr->getFps() >=
Fps::fromValue(mConfig.frameRateMultipleThreshold);
}();
// Now we can add the fixed rate layers score
- for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+ for (auto& [frameRateMode, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold;
if (maxScoreAboveThreshold) {
overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold;
}
- ALOGV("%s adjusted overallScore is %.4f", to_string(modeIt->second->getFps()).c_str(),
- overallScore);
+ ALOGV("%s (%s) adjusted overallScore is %.4f", to_string(frameRateMode.fps).c_str(),
+ to_string(frameRateMode.modePtr->getFps()).c_str(), overallScore);
}
// Now that we scored all the refresh rates we need to pick the one that got the highest
@@ -552,12 +651,12 @@
std::sort(scores.begin(), scores.end(),
RefreshRateScoreComparator{.refreshRateOrder = refreshRateOrder});
- RefreshRateRanking ranking;
+ FrameRateRanking ranking;
ranking.reserve(scores.size());
std::transform(scores.begin(), scores.end(), back_inserter(ranking),
[](const RefreshRateScore& score) {
- return ScoredRefreshRate{score.modeIt->second, score.overallScore};
+ return ScoredFrameRate{score.frameRateMode, score.overallScore};
});
const bool noLayerScore = std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) {
@@ -569,7 +668,7 @@
// range instead of picking a random score from the app range.
if (noLayerScore) {
ALOGV("Layers not scored");
- return {rankRefreshRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Descending), kNoSignals};
} else {
return {ranking, kNoSignals};
}
@@ -580,7 +679,7 @@
// vote we should not change it if we get a touch event. Only apply touch boost if it will
// actually increase the refresh rate over the normal selection.
const bool touchBoostForExplicitExact = [&] {
- if (supportsFrameRateOverrideByContent()) {
+ if (supportsAppFrameRateOverrideByContent()) {
// Enable touch boost if there are other layers besides exact
return explicitExact + noVoteLayers != layers.size();
} else {
@@ -589,12 +688,11 @@
}
}();
- const auto touchRefreshRates = rankRefreshRates(anchorGroup, RefreshRateOrder::Descending);
-
+ const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
using fps_approx_ops::operator<;
if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
- scores.front().modeIt->second->getFps() < touchRefreshRates.front().modePtr->getFps()) {
+ scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
ALOGV("Touch Boost");
return {touchRefreshRates, GlobalSignals{.touch = true}};
}
@@ -603,7 +701,7 @@
// current config
if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) {
const auto preferredDisplayMode = activeMode.getId();
- return {rankRefreshRates(anchorGroup, RefreshRateOrder::Ascending, preferredDisplayMode),
+ return {rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, preferredDisplayMode),
kNoSignals};
}
@@ -649,20 +747,13 @@
GlobalSignals globalSignals) const
-> UidToFrameRateOverride {
ATRACE_CALL();
- ALOGV("%s: %zu layers", __func__, layers.size());
-
- std::lock_guard lock(mLock);
-
- // Prepare a set of supported display refresh rates for easy lookup
- constexpr size_t kStaticCapacity = 8;
- ftl::SmallMap<Fps, ftl::Unit, kStaticCapacity, FpsApproxEqual> supportedDisplayRefreshRates;
- if (mConfig.enableFrameRateOverride ==
- Config::FrameRateOverride::EnabledForNativeRefreshRates) {
- for (const auto& [_, modePtr] : mDisplayModes) {
- supportedDisplayRefreshRates.try_emplace(modePtr->getFps(), ftl::unit);
- }
+ if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Disabled) {
+ return {};
}
+ ALOGV("%s: %zu layers", __func__, layers.size());
+ std::lock_guard lock(mLock);
+
const auto* policyPtr = getCurrentPolicyLocked();
// We don't want to run lower than 30fps
const Fps minFrameRate = std::max(policyPtr->appRequestRanges.render.min, 30_Hz, isApproxLess);
@@ -676,8 +767,8 @@
for (unsigned n = numMultiples; n > 0; n--) {
const Fps divisor = displayRefreshRate / n;
if (mConfig.enableFrameRateOverride ==
- Config::FrameRateOverride::EnabledForNativeRefreshRates &&
- !supportedDisplayRefreshRates.contains(divisor)) {
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates &&
+ !isNativeRefreshRate(divisor)) {
continue;
}
@@ -736,7 +827,7 @@
[](const auto& lhsPair, const auto& rhsPair) {
const float lhs = lhsPair.second;
const float rhs = rhsPair.second;
- return lhs < rhs && !ScoredRefreshRate::scoresEqual(lhs, rhs);
+ return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs);
});
ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
frameRateOverrides.emplace(uid, overrideFps);
@@ -765,10 +856,9 @@
const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const {
const auto& activeMode = *getActiveModeItLocked()->second;
- for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) {
- const auto& mode = modeIt->second;
- if (activeMode.getGroup() == mode->getGroup()) {
- return mode;
+ for (const FrameRateMode& mode : mPrimaryFrameRates) {
+ if (activeMode.getGroup() == mode.modePtr->getGroup()) {
+ return mode.modePtr;
}
}
@@ -776,55 +866,72 @@
to_string(activeMode).c_str());
// Default to the lowest refresh rate.
- return mPrimaryRefreshRates.front()->second;
+ return mPrimaryFrameRates.front().modePtr;
}
const DisplayModePtr& RefreshRateSelector::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
- for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) {
- const auto& mode = (*it)->second;
- if (anchorGroup == mode->getGroup()) {
- return mode;
+ const DisplayModePtr* maxByAnchor = &mPrimaryFrameRates.back().modePtr;
+ const DisplayModePtr* max = &mPrimaryFrameRates.back().modePtr;
+
+ bool maxByAnchorFound = false;
+ for (auto it = mPrimaryFrameRates.rbegin(); it != mPrimaryFrameRates.rend(); ++it) {
+ using namespace fps_approx_ops;
+ if (it->modePtr->getFps() > (*max)->getFps()) {
+ max = &it->modePtr;
}
+
+ if (anchorGroup == it->modePtr->getGroup() &&
+ it->modePtr->getFps() >= (*maxByAnchor)->getFps()) {
+ maxByAnchorFound = true;
+ maxByAnchor = &it->modePtr;
+ }
+ }
+
+ if (maxByAnchorFound) {
+ return *maxByAnchor;
}
ALOGE("Can't find max refresh rate by policy with the same group %d", anchorGroup);
// Default to the highest refresh rate.
- return mPrimaryRefreshRates.back()->second;
+ return *max;
}
-auto RefreshRateSelector::rankRefreshRates(
- std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
- std::optional<DisplayModeId> preferredDisplayModeOpt) const -> RefreshRateRanking {
- std::deque<ScoredRefreshRate> ranking;
-
- const auto rankRefreshRate = [&](DisplayModeIterator it) REQUIRES(mLock) {
- const auto& mode = it->second;
- if (anchorGroupOpt && mode->getGroup() != anchorGroupOpt) {
+auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt,
+ RefreshRateOrder refreshRateOrder,
+ std::optional<DisplayModeId> preferredDisplayModeOpt) const
+ -> FrameRateRanking {
+ const char* const whence = __func__;
+ std::deque<ScoredFrameRate> ranking;
+ const auto rankFrameRate = [&](const FrameRateMode& frameRateMode) REQUIRES(mLock) {
+ const auto& modePtr = frameRateMode.modePtr;
+ if (anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) {
return;
}
- float score = calculateRefreshRateScoreForFps(mode->getFps());
+ float score = calculateDistanceScoreFromMax(frameRateMode.fps);
const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
if (inverseScore) {
score = 1.0f / score;
}
if (preferredDisplayModeOpt) {
- if (*preferredDisplayModeOpt == mode->getId()) {
+ if (*preferredDisplayModeOpt == modePtr->getId()) {
constexpr float kScore = std::numeric_limits<float>::max();
- ranking.push_front(ScoredRefreshRate{mode, kScore});
+ ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore});
return;
}
constexpr float kNonPreferredModePenalty = 0.95f;
score *= kNonPreferredModePenalty;
}
- ranking.push_back(ScoredRefreshRate{mode, score});
+ ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(),
+ to_string(frameRateMode.fps).c_str(), to_string(modePtr->getFps()).c_str(), score);
+ ranking.emplace_back(ScoredFrameRate{frameRateMode, score});
};
if (refreshRateOrder == RefreshRateOrder::Ascending) {
- std::for_each(mPrimaryRefreshRates.begin(), mPrimaryRefreshRates.end(), rankRefreshRate);
+ std::for_each(mPrimaryFrameRates.begin(), mPrimaryFrameRates.end(), rankFrameRate);
} else {
- std::for_each(mPrimaryRefreshRates.rbegin(), mPrimaryRefreshRates.rend(), rankRefreshRate);
+ std::for_each(mPrimaryFrameRates.rbegin(), mPrimaryFrameRates.rend(), rankFrameRate);
}
if (!ranking.empty() || !anchorGroupOpt) {
@@ -836,7 +943,7 @@
refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value());
constexpr std::optional<int> kNoAnchorGroup = std::nullopt;
- return rankRefreshRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt);
+ return rankFrameRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt);
}
DisplayModePtr RefreshRateSelector::getActiveModePtr() const {
@@ -858,9 +965,9 @@
void RefreshRateSelector::setActiveModeId(DisplayModeId modeId) {
std::lock_guard lock(mLock);
- // Invalidate the cached invocation to getRankedRefreshRates. This forces
- // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
- mGetRankedRefreshRatesCache.reset();
+ // Invalidate the cached invocation to getRankedFrameRates. This forces
+ // the refresh rate to be recomputed on the next call to getRankedFrameRates.
+ mGetRankedFrameRatesCache.reset();
mActiveModeIt = mDisplayModes.find(modeId);
LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
@@ -895,16 +1002,15 @@
void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
std::lock_guard lock(mLock);
- // Invalidate the cached invocation to getRankedRefreshRates. This forces
- // the refresh rate to be recomputed on the next call to getRankedRefreshRates.
- mGetRankedRefreshRatesCache.reset();
+ // Invalidate the cached invocation to getRankedFrameRates. This forces
+ // the refresh rate to be recomputed on the next call to getRankedFrameRates.
+ mGetRankedFrameRatesCache.reset();
mDisplayModes = std::move(modes);
mActiveModeIt = mDisplayModes.find(activeModeId);
LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
- const auto sortedModes =
- sortByRefreshRate(mDisplayModes, [](const DisplayMode&) { return true; });
+ const auto sortedModes = sortByRefreshRate(mDisplayModes);
mMinRefreshRateModeIt = sortedModes.front();
mMaxRefreshRateModeIt = sortedModes.back();
@@ -915,15 +1021,23 @@
mFrameRateOverrideConfig = [&] {
switch (mConfig.enableFrameRateOverride) {
case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverride:
case Config::FrameRateOverride::Enabled:
return mConfig.enableFrameRateOverride;
- case Config::FrameRateOverride::EnabledForNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
return shouldEnableFrameRateOverride(sortedModes)
- ? Config::FrameRateOverride::EnabledForNativeRefreshRates
+ ? Config::FrameRateOverride::AppOverrideNativeRefreshRates
: Config::FrameRateOverride::Disabled;
}
}();
+ if (mConfig.enableFrameRateOverride ==
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates) {
+ for (const auto& [_, mode] : mDisplayModes) {
+ mAppOverrideNativeRefreshRates.try_emplace(mode->getFps(), ftl::unit);
+ }
+ }
+
constructAvailableRefreshRates();
}
@@ -939,9 +1053,13 @@
return false;
}
- using namespace fps_approx_ops;
- return policy.appRequestRanges.physical.min <= policy.primaryRanges.physical.min &&
- policy.appRequestRanges.physical.max >= policy.primaryRanges.physical.max;
+ const auto& primaryRanges = policy.primaryRanges;
+ const auto& appRequestRanges = policy.appRequestRanges;
+ ALOGE_IF(!appRequestRanges.physical.includes(primaryRanges.physical),
+ "Physical range is invalid");
+ ALOGE_IF(!appRequestRanges.render.includes(primaryRanges.render), "Render range is invalid");
+
+ return primaryRanges.valid() && appRequestRanges.valid();
}
auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyResult {
@@ -979,7 +1097,7 @@
return SetPolicyResult::Invalid;
}
- mGetRankedRefreshRatesCache.reset();
+ mGetRankedFrameRatesCache.reset();
if (*getCurrentPolicyLocked() == oldPolicy) {
return SetPolicyResult::Unchanged;
@@ -1016,9 +1134,9 @@
bool RefreshRateSelector::isModeAllowed(DisplayModeId modeId) const {
std::lock_guard lock(mLock);
- return std::any_of(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
- [modeId](DisplayModeIterator modeIt) {
- return modeIt->second->getId() == modeId;
+ return std::any_of(mAppRequestFrameRates.begin(), mAppRequestFrameRates.end(),
+ [modeId](const FrameRateMode& frameRateMode) {
+ return frameRateMode.modePtr->getId() == modeId;
});
}
@@ -1029,33 +1147,35 @@
const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
- const auto filterRefreshRates = [&](FpsRange range, const char* rangeName) REQUIRES(mLock) {
- const auto filter = [&](const DisplayMode& mode) {
+ const auto filterRefreshRates = [&](const FpsRanges& ranges,
+ const char* rangeName) REQUIRES(mLock) {
+ const auto filterModes = [&](const DisplayMode& mode) {
return mode.getResolution() == defaultMode->getResolution() &&
mode.getDpi() == defaultMode->getDpi() &&
(policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) &&
- range.includes(mode.getFps());
+ ranges.physical.includes(mode.getFps()) &&
+ (supportsFrameRateOverride() || ranges.render.includes(mode.getFps()));
};
- const auto modes = sortByRefreshRate(mDisplayModes, filter);
- LOG_ALWAYS_FATAL_IF(modes.empty(), "No matching modes for %s range %s", rangeName,
- to_string(range).c_str());
+ const auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
+ LOG_ALWAYS_FATAL_IF(frameRateModes.empty(),
+ "No matching frame rate modes for %s physicalRange %s", rangeName,
+ to_string(ranges.physical).c_str());
const auto stringifyModes = [&] {
std::string str;
- for (const auto modeIt : modes) {
- str += to_string(modeIt->second->getFps());
- str.push_back(' ');
+ for (const auto& frameRateMode : frameRateModes) {
+ str += to_string(frameRateMode) + " ";
}
return str;
};
- ALOGV("%s refresh rates: %s", rangeName, stringifyModes().c_str());
+ ALOGV("%s render rates: %s", rangeName, stringifyModes().c_str());
- return modes;
+ return frameRateModes;
};
- mPrimaryRefreshRates = filterRefreshRates(policy->primaryRanges.physical, "primary");
- mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRanges.physical, "app request");
+ mPrimaryFrameRates = filterRefreshRates(policy->primaryRanges, "primary");
+ mAppRequestFrameRates = filterRefreshRates(policy->appRequestRanges, "app request");
}
Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 65ee487..89ebeea 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -24,9 +24,11 @@
#include <ftl/concat.h>
#include <ftl/optional.h>
+#include <ftl/unit.h>
#include <gui/DisplayEventReceiver.h>
#include <scheduler/Fps.h>
+#include <scheduler/FrameRateMode.h>
#include <scheduler/Seamlessness.h>
#include "DisplayHardware/DisplayMode.h"
@@ -58,6 +60,9 @@
static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
std::chrono::nanoseconds(800us).count();
+ // The lowest Render Frame Rate that will ever be selected
+ static constexpr Fps kMinSupportedFrameRate = 20_Hz;
+
class Policy {
static constexpr int kAllowGroupSwitchingDefault = false;
@@ -196,12 +201,12 @@
}
};
- struct ScoredRefreshRate {
- DisplayModePtr modePtr;
+ struct ScoredFrameRate {
+ FrameRateMode frameRateMode;
float score = 0.0f;
- bool operator==(const ScoredRefreshRate& other) const {
- return modePtr == other.modePtr && score == other.score;
+ bool operator==(const ScoredFrameRate& other) const {
+ return frameRateMode == other.frameRateMode && score == other.score;
}
static bool scoresEqual(float lhs, float rhs) {
@@ -210,25 +215,25 @@
}
struct DescendingScore {
- bool operator()(const ScoredRefreshRate& lhs, const ScoredRefreshRate& rhs) const {
+ bool operator()(const ScoredFrameRate& lhs, const ScoredFrameRate& rhs) const {
return lhs.score > rhs.score && !scoresEqual(lhs.score, rhs.score);
}
};
};
- using RefreshRateRanking = std::vector<ScoredRefreshRate>;
+ using FrameRateRanking = std::vector<ScoredFrameRate>;
- struct RankedRefreshRates {
- RefreshRateRanking ranking; // Ordered by descending score.
+ struct RankedFrameRates {
+ FrameRateRanking ranking; // Ordered by descending score.
GlobalSignals consideredSignals;
- bool operator==(const RankedRefreshRates& other) const {
+ bool operator==(const RankedFrameRates& other) const {
return ranking == other.ranking && consideredSignals == other.consideredSignals;
}
};
- RankedRefreshRates getRankedRefreshRates(const std::vector<LayerRequirement>&,
- GlobalSignals) const EXCLUDES(mLock);
+ RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const
+ EXCLUDES(mLock);
FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
std::lock_guard lock(mLock);
@@ -257,9 +262,12 @@
// Override the frame rate for an app to a value which is also
// a display refresh rate
- EnabledForNativeRefreshRates,
+ AppOverrideNativeRefreshRates,
// Override the frame rate for an app to any value
+ AppOverride,
+
+ // Override the frame rate for all apps and all values.
Enabled,
ftl_last = Enabled
@@ -291,10 +299,13 @@
// Returns whether switching modes (refresh rate or resolution) is possible.
// TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
- // differ in resolution.
+ // differ in resolution. Once Config::FrameRateOverride::Enabled becomes the default,
+ // we can probably remove canSwitch altogether since all devices will be able
+ // to switch to a frame rate divisor.
bool canSwitch() const EXCLUDES(mLock) {
std::lock_guard lock(mLock);
- return mDisplayModes.size() > 1;
+ return mDisplayModes.size() > 1 ||
+ mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled;
}
// Class to enumerate options around toggling the kernel timer on and off.
@@ -307,10 +318,14 @@
// refresh rates.
KernelIdleTimerAction getIdleTimerAction() const;
- bool supportsFrameRateOverrideByContent() const {
+ bool supportsAppFrameRateOverrideByContent() const {
return mFrameRateOverrideConfig != Config::FrameRateOverride::Disabled;
}
+ bool supportsFrameRateOverride() const {
+ return mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled;
+ }
+
// Return the display refresh rate divisor to match the layer
// frame rate, or 0 if the display refresh rate is not a multiple of the
// layer refresh rate.
@@ -387,8 +402,8 @@
// See mActiveModeIt for thread safety.
DisplayModeIterator getActiveModeItLocked() const REQUIRES(mLock);
- RankedRefreshRates getRankedRefreshRatesLocked(const std::vector<LayerRequirement>&,
- GlobalSignals) const REQUIRES(mLock);
+ RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const REQUIRES(mLock);
// Returns number of display frames and remainder when dividing the layer refresh period by
// display refresh period.
@@ -404,18 +419,24 @@
struct RefreshRateScoreComparator;
- enum class RefreshRateOrder { Ascending, Descending };
+ enum class RefreshRateOrder {
+ Ascending,
+ Descending,
+
+ ftl_last = Descending
+ };
// Only uses the primary range, not the app request range.
- RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt, RefreshRateOrder,
- std::optional<DisplayModeId> preferredDisplayModeOpt =
- std::nullopt) const REQUIRES(mLock);
+ FrameRateRanking rankFrameRates(
+ std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
+ std::optional<DisplayModeId> preferredDisplayModeOpt = std::nullopt) const
+ REQUIRES(mLock);
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
// Returns the refresh rate score as a ratio to max refresh rate, which has a score of 1.
- float calculateRefreshRateScoreForFps(Fps refreshRate) const REQUIRES(mLock);
+ float calculateDistanceScoreFromMax(Fps refreshRate) const REQUIRES(mLock);
// calculates a score for a layer. Used to determine the display refresh rate
// and the frame rate override for certains applications.
float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
@@ -436,11 +457,27 @@
: mIdleTimerCallbacks->platform;
}
+ bool isNativeRefreshRate(Fps fps) const REQUIRES(mLock) {
+ LOG_ALWAYS_FATAL_IF(mConfig.enableFrameRateOverride !=
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates,
+ "should only be called when "
+ "Config::FrameRateOverride::AppOverrideNativeRefreshRates is used");
+ return mAppOverrideNativeRefreshRates.contains(fps);
+ }
+
+ std::vector<FrameRateMode> createFrameRateModes(
+ std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange&) const
+ REQUIRES(mLock);
+
// The display modes of the active display. The DisplayModeIterators below are pointers into
// this container, so must be invalidated whenever the DisplayModes change. The Policy below
// is also dependent, so must be reset as well.
DisplayModes mDisplayModes GUARDED_BY(mLock);
+ // Set of supported display refresh rates for easy lookup
+ // when FrameRateOverride::AppOverrideNativeRefreshRates is in use.
+ ftl::SmallMap<Fps, ftl::Unit, 8, FpsApproxEqual> mAppOverrideNativeRefreshRates;
+
// Written under mLock exclusively from kMainThreadContext, so reads from kMainThreadContext
// need not be under mLock.
DisplayModeIterator mActiveModeIt GUARDED_BY(mLock) GUARDED_BY(kMainThreadContext);
@@ -449,8 +486,8 @@
DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock);
// Display modes that satisfy the Policy's ranges, filtered and sorted by refresh rate.
- std::vector<DisplayModeIterator> mPrimaryRefreshRates GUARDED_BY(mLock);
- std::vector<DisplayModeIterator> mAppRequestRefreshRates GUARDED_BY(mLock);
+ std::vector<FrameRateMode> mPrimaryFrameRates GUARDED_BY(mLock);
+ std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock);
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
@@ -466,11 +503,11 @@
const Config mConfig;
Config::FrameRateOverride mFrameRateOverrideConfig;
- struct GetRankedRefreshRatesCache {
+ struct GetRankedFrameRatesCache {
std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
- RankedRefreshRates result;
+ RankedFrameRates result;
};
- mutable std::optional<GetRankedRefreshRatesCache> mGetRankedRefreshRatesCache GUARDED_BY(mLock);
+ mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache GUARDED_BY(mLock);
// Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed.
std::mutex mIdleTimerCallbacksMutex;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f1fcc88..0c541f9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -152,7 +152,7 @@
std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
const bool supportsFrameRateOverrideByContent =
- leaderSelectorPtr()->supportsFrameRateOverrideByContent();
+ leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
return mFrameRateOverrideMappings
.getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
}
@@ -268,7 +268,7 @@
void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
const bool supportsFrameRateOverrideByContent =
- leaderSelectorPtr()->supportsFrameRateOverrideByContent();
+ leaderSelectorPtr()->supportsAppFrameRateOverrideByContent();
std::vector<FrameRateOverride> overrides =
mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -707,7 +707,7 @@
auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
ATRACE_CALL();
- using RankedRefreshRates = RefreshRateSelector::RankedRefreshRates;
+ using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
// Tallies the score of a refresh rate across `displayCount` displays.
@@ -726,9 +726,10 @@
for (const auto& [id, selectorPtr] : mRefreshRateSelectors) {
auto rankedRefreshRates =
- selectorPtr->getRankedRefreshRates(mPolicy.contentRequirements, globalSignals);
+ selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals);
- for (const auto& [modePtr, score] : rankedRefreshRates.ranking) {
+ for (const auto& [frameRateMode, score] : rankedRefreshRates.ranking) {
+ const auto& modePtr = frameRateMode.modePtr;
const auto [it, inserted] = refreshRateTallies.try_emplace(modePtr->getFps(), score);
if (!inserted) {
@@ -771,16 +772,18 @@
for (auto& [ranking, signals] : perDisplayRanking) {
if (!chosenFps) {
- auto& [modePtr, _] = ranking.front();
+ const auto& [frameRateMode, _] = ranking.front();
+ const auto& modePtr = frameRateMode.modePtr;
modeChoices.try_emplace(modePtr->getPhysicalDisplayId(),
- DisplayModeChoice{std::move(modePtr), signals});
+ DisplayModeChoice{modePtr, signals});
continue;
}
- for (auto& [modePtr, _] : ranking) {
+ for (auto& [frameRateMode, _] : ranking) {
+ const auto& modePtr = frameRateMode.modePtr;
if (modePtr->getFps() == *chosenFps) {
modeChoices.try_emplace(modePtr->getPhysicalDisplayId(),
- DisplayModeChoice{std::move(modePtr), signals});
+ DisplayModeChoice{modePtr, signals});
break;
}
}
@@ -804,10 +807,10 @@
if (mPolicy.mode) {
const auto ranking =
leaderSelectorPtr()
- ->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals())
+ ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
.ranking;
- mPolicy.mode = ranking.front().modePtr;
+ mPolicy.mode = ranking.front().frameRateMode.modePtr;
}
return mPolicy.mode;
}
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 31b1d69..5522ff8 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -66,6 +66,7 @@
Fps max = Fps::fromValue(std::numeric_limits<float>::max());
bool includes(Fps) const;
+ bool includes(FpsRange) const;
};
struct FpsRanges {
@@ -75,6 +76,8 @@
// the range of frame rates that refers to the render rate, which is
// the rate that frames are swapped.
FpsRange render;
+
+ bool valid() const;
};
static_assert(std::is_trivially_copyable_v<Fps>);
@@ -159,6 +162,16 @@
return min <= fps && fps <= max;
}
+inline bool FpsRange::includes(FpsRange range) const {
+ using namespace fps_approx_ops;
+ return min <= range.min && max >= range.max;
+}
+
+inline bool FpsRanges::valid() const {
+ using fps_approx_ops::operator>=;
+ return physical.max >= render.max;
+}
+
struct FpsApproxEqual {
bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
};
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
new file mode 100644
index 0000000..670ab45
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <scheduler/Fps.h>
+
+// TODO(b/241285191): Pull this to <ui/DisplayMode.h>
+#include "DisplayHardware/DisplayMode.h"
+
+namespace android::scheduler {
+
+struct FrameRateMode {
+ Fps fps; // The render frame rate, which is a divisor of modePtr->getFps().
+ DisplayModePtr modePtr;
+
+ bool operator==(const FrameRateMode& other) const {
+ return isApproxEqual(fps, other.fps) && modePtr == other.modePtr;
+ }
+
+ bool operator!=(const FrameRateMode& other) const { return !(*this == other); }
+};
+
+inline std::string to_string(const FrameRateMode& mode) {
+ if (mode.modePtr) {
+ return to_string(mode.fps) + " (" + to_string(mode.modePtr->getFps()) + ")";
+ }
+ return "{invalid}";
+}
+
+} // namespace android::scheduler
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9f26129..032541c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2793,7 +2793,11 @@
}
if (sysprop::frame_rate_override_for_native_rates(true)) {
- return Config::FrameRateOverride::EnabledForNativeRefreshRates;
+ return Config::FrameRateOverride::AppOverrideNativeRefreshRates;
+ }
+
+ if (!base::GetBoolProperty("debug.sf.frame_rate_override_global"s, false)) {
+ return Config::FrameRateOverride::AppOverride;
}
return Config::FrameRateOverride::Enabled;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index bd11a37..950e6d3 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -353,7 +353,7 @@
const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
- refreshRateSelector.getRankedRefreshRates(layers, globalSignals);
+ refreshRateSelector.getRankedFrameRates(layers, globalSignals);
layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength);
layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>();
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index cedb7eb..8757e63 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -26,6 +26,7 @@
#include <log/log.h>
#include <ui/Size.h>
+#include <scheduler/FrameRateMode.h>
#include "DisplayHardware/HWC2.h"
#include "FpsOps.h"
#include "Scheduler/RefreshRateSelector.h"
@@ -44,9 +45,13 @@
using mock::createDisplayMode;
+// Use a C style macro to keep the line numbers printed in gtest
+#define EXPECT_SCORED_FRAME_RATE(modePtr, fps, scored) \
+ EXPECT_EQ((FrameRateMode{(fps), (modePtr)}), (scored).frameRateMode)
+
struct TestableRefreshRateSelector : RefreshRateSelector {
+ using RefreshRateSelector::FrameRateRanking;
using RefreshRateSelector::RefreshRateOrder;
- using RefreshRateSelector::RefreshRateRanking;
using RefreshRateSelector::RefreshRateSelector;
@@ -80,36 +85,41 @@
return getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
}
- RefreshRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
- RefreshRateOrder refreshRateOrder) const {
+ FrameRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
+ RefreshRateOrder refreshRateOrder) const {
std::lock_guard lock(mLock);
- return RefreshRateSelector::rankRefreshRates(anchorGroupOpt, refreshRateOrder);
+ return RefreshRateSelector::rankFrameRates(anchorGroupOpt, refreshRateOrder);
}
const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
- using RefreshRateSelector::GetRankedRefreshRatesCache;
- auto& mutableGetRankedRefreshRatesCache() { return mGetRankedRefreshRatesCache; }
+ using RefreshRateSelector::GetRankedFrameRatesCache;
+ auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
- auto getRankedRefreshRates(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const {
- const auto result = RefreshRateSelector::getRankedRefreshRates(layers, signals);
+ auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
+ GlobalSignals signals) const {
+ const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals);
EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(),
- ScoredRefreshRate::DescendingScore{}));
+ ScoredFrameRate::DescendingScore{}));
return result;
}
auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers,
GlobalSignals signals) const {
- const auto [ranking, consideredSignals] = getRankedRefreshRates(layers, signals);
+ const auto [ranking, consideredSignals] = getRankedFrameRates(layers, signals);
return std::make_pair(ranking, consideredSignals);
}
- DisplayModePtr getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
- GlobalSignals signals = {}) const {
- return getRankedRefreshRates(layers, signals).ranking.front().modePtr;
+ DisplayModePtr getBestFrameRateMode(const std::vector<LayerRequirement>& layers = {},
+ GlobalSignals signals = {}) const {
+ return getRankedFrameRates(layers, signals).ranking.front().frameRateMode.modePtr;
+ }
+
+ ScoredFrameRate getBestScoredFrameRate(const std::vector<LayerRequirement>& layers = {},
+ GlobalSignals signals = {}) const {
+ return getRankedFrameRates(layers, signals).ranking.front();
}
SetPolicyResult setPolicy(const PolicyVariant& policy) {
@@ -120,9 +130,11 @@
SetPolicyResult setDisplayManagerPolicy(const DisplayManagerPolicy& policy) {
return setPolicy(policy);
}
+
+ const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; }
};
-class RefreshRateSelectorTest : public testing::Test {
+class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> {
protected:
using RefreshRateOrder = TestableRefreshRateSelector::RefreshRateOrder;
@@ -140,6 +152,7 @@
static constexpr DisplayModeId kModeId24Frac{8};
static constexpr DisplayModeId kModeId30Frac{9};
static constexpr DisplayModeId kModeId60Frac{10};
+ static constexpr DisplayModeId kModeId35{11};
static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
static inline const DisplayModePtr kMode60Frac = createDisplayMode(kModeId60Frac, 59.94_Hz);
@@ -156,12 +169,14 @@
static inline const DisplayModePtr kMode30Frac = createDisplayMode(kModeId30Frac, 29.97_Hz);
static inline const DisplayModePtr kMode25 = createDisplayMode(kModeId25, 25_Hz);
static inline const DisplayModePtr kMode25_G1 = createDisplayMode(kModeId25, 25_Hz, 1);
+ static inline const DisplayModePtr kMode35 = createDisplayMode(kModeId35, 35_Hz);
static inline const DisplayModePtr kMode50 = createDisplayMode(kModeId50, 50_Hz);
static inline const DisplayModePtr kMode24 = createDisplayMode(kModeId24, 24_Hz);
static inline const DisplayModePtr kMode24Frac = createDisplayMode(kModeId24Frac, 23.976_Hz);
// Test configurations.
static inline const DisplayModes kModes_60 = makeModes(kMode60);
+ static inline const DisplayModes kModes_35_60_90 = makeModes(kMode35, kMode60, kMode90);
static inline const DisplayModes kModes_60_90 = makeModes(kMode60, kMode90);
static inline const DisplayModes kModes_60_90_G1 = makeModes(kMode60, kMode90_G1);
static inline const DisplayModes kModes_60_90_4K = makeModes(kMode60, kMode90_4K);
@@ -185,6 +200,13 @@
static inline const DisplayModes kModes_24_25_30_50_60_Frac =
makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60,
kMode60Frac);
+
+ static TestableRefreshRateSelector createSelector(DisplayModes modes,
+ DisplayModeId activeModeId,
+ Config config = {}) {
+ config.enableFrameRateOverride = GetParam();
+ return TestableRefreshRateSelector(modes, activeModeId, config);
+ }
};
RefreshRateSelectorTest::RefreshRateSelectorTest() {
@@ -201,13 +223,23 @@
namespace {
-TEST_F(RefreshRateSelectorTest, oneMode_canSwitch) {
- RefreshRateSelector selector(kModes_60, kModeId60);
- EXPECT_FALSE(selector.canSwitch());
+INSTANTIATE_TEST_SUITE_P(PerOverrideConfig, RefreshRateSelectorTest,
+ testing::Values(Config::FrameRateOverride::Disabled,
+ Config::FrameRateOverride::AppOverrideNativeRefreshRates,
+ Config::FrameRateOverride::AppOverride,
+ Config::FrameRateOverride::Enabled));
+
+TEST_P(RefreshRateSelectorTest, oneMode_canSwitch) {
+ auto selector = createSelector(kModes_60, kModeId60);
+ if (GetParam() == Config::FrameRateOverride::Enabled) {
+ EXPECT_TRUE(selector.canSwitch());
+ } else {
+ EXPECT_FALSE(selector.canSwitch());
+ }
}
-TEST_F(RefreshRateSelectorTest, invalidPolicy) {
- TestableRefreshRateSelector selector(kModes_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, invalidPolicy) {
+ auto selector = createSelector(kModes_60, kModeId60);
EXPECT_EQ(SetPolicyResult::Invalid,
selector.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}));
@@ -215,8 +247,8 @@
selector.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}));
}
-TEST_F(RefreshRateSelectorTest, unchangedPolicy) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, unchangedPolicy) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
@@ -236,8 +268,8 @@
selector.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}}));
}
-TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
const auto minRate = selector.getMinSupportedRefreshRate();
const auto performanceRate = selector.getMaxSupportedRefreshRate();
@@ -252,8 +284,8 @@
EXPECT_EQ(performanceRateByPolicy, performanceRate);
}
-TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
const auto minRate = selector.getMinRefreshRateByPolicy();
const auto performanceRate = selector.getMaxSupportedRefreshRate();
@@ -276,8 +308,8 @@
EXPECT_EQ(kMode90_G1, performanceRate90);
}
-TEST_F(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
- TestableRefreshRateSelector selector(kModes_60_90_4K, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
+ auto selector = createSelector(kModes_60_90_4K, kModeId60);
const auto minRate = selector.getMinRefreshRateByPolicy();
const auto performanceRate = selector.getMaxSupportedRefreshRate();
@@ -300,8 +332,8 @@
EXPECT_EQ(kMode90_4K, performanceRate90);
}
-TEST_F(RefreshRateSelectorTest, twoModes_policyChange) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_policyChange) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
const auto minRate = selector.getMinRefreshRateByPolicy();
const auto performanceRate = selector.getMaxRefreshRateByPolicy();
@@ -319,8 +351,8 @@
EXPECT_EQ(kMode60, performanceRate60);
}
-TEST_F(RefreshRateSelectorTest, twoModes_getActiveMode) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_getActiveMode) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
{
const auto& mode = selector.getActiveMode();
EXPECT_EQ(mode.getId(), kModeId60);
@@ -340,31 +372,31 @@
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_noLayers) {
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_noLayers) {
{
- TestableRefreshRateSelector selector(kModes_60_72_90, kModeId72);
+ auto selector = createSelector(kModes_60_72_90, kModeId72);
// If there are no layers we select the default frame rate, which is the max of the primary
// range.
- EXPECT_EQ(kMode90, selector.getBestRefreshRate());
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode());
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
- EXPECT_EQ(kMode60, selector.getBestRefreshRate());
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode());
}
{
// We select max even when this will cause a non-seamless switch.
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
constexpr bool kAllowGroupSwitching = true;
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy(
{kModeId90, {0_Hz, 90_Hz}, kAllowGroupSwitching}));
- EXPECT_EQ(kMode90_G1, selector.getBestRefreshRate());
+ EXPECT_EQ(kMode90_G1, selector.getBestFrameRateMode());
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_exactDontChangeRefreshRateWhenNotInPolicy) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId72);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_exactDontChangeRefreshRateWhenNotInPolicy) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId72);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].vote = LayerVoteType::ExplicitExact;
@@ -372,188 +404,187 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}}));
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_90) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_90) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.name = "";
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}));
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_multipleThreshold_60_90) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60,
- {.frameRateMultipleThreshold = 90});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multipleThreshold_60_90) {
+ auto selector = createSelector(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90});
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_60_72_90) {
- TestableRefreshRateSelector selector(kModes_60_72_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_72_90) {
+ auto selector = createSelector(kModes_60_72_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90_120) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90_120) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -563,23 +594,23 @@
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_90_120_DifferentTypes) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -591,7 +622,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -599,7 +630,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -607,7 +638,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -615,7 +646,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -623,7 +654,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -631,7 +662,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
@@ -639,7 +670,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -647,7 +678,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -655,12 +686,13 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60,
- {.frameRateMultipleThreshold = 120});
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_30_60_90_120_DifferentTypes_multipleThreshold) {
+ auto selector =
+ createSelector(kModes_30_60_72_90_120, kModeId60, {.frameRateMultipleThreshold = 120});
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -673,7 +705,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -681,7 +713,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -689,7 +721,7 @@
lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -697,7 +729,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -705,7 +737,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -713,7 +745,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
@@ -721,7 +753,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -729,7 +761,7 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -737,14 +769,14 @@
lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -752,7 +784,7 @@
lr2.desiredRefreshRate = 120_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "120Hz ExplicitDefault";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -760,7 +792,7 @@
lr2.desiredRefreshRate = 120_Hz;
lr2.vote = LayerVoteType::ExplicitExact;
lr2.name = "120Hz ExplicitExact";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 10_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -768,7 +800,7 @@
lr2.desiredRefreshRate = 120_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "120Hz ExplicitExact";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
lr1.desiredRefreshRate = 30_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -779,86 +811,86 @@
lr3.vote = LayerVoteType::Heuristic;
lr3.desiredRefreshRate = 120_Hz;
lr3.name = "120Hz Heuristic";
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60) {
- TestableRefreshRateSelector selector(kModes_30_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60) {
+ auto selector = createSelector(kModes_30_60, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_30_60_72_90) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90) {
+ auto selector = createSelector(kModes_30_60_72_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr.desiredRefreshRate = 24_Hz;
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.name = "24Hz ExplicitExactOrMultiple";
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_PriorityTest) {
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_PriorityTest) {
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -866,43 +898,43 @@
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Max;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 24_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 15_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 30_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 45_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
@@ -910,15 +942,14 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = selector.getBestRefreshRate(layers);
+ const auto mode = selector.getBestFrameRateMode(layers);
EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
<< to_string(mode->getFps());
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) {
- TestableRefreshRateSelector selector(kModes_60_120, kModeId60,
- {.frameRateMultipleThreshold = 120});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo_multipleThreshold_60_120) {
+ auto selector = createSelector(kModes_60_120, kModeId60, {.frameRateMultipleThreshold = 120});
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
@@ -926,14 +957,14 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = selector.getBestRefreshRate(layers);
+ const auto mode = selector.getBestFrameRateMode(layers);
EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
<< to_string(mode->getFps());
}
}
-TEST_F(RefreshRateSelectorTest, twoModes_getBestRefreshRate_Explicit) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, twoModes_getBestFrameRateMode_Explicit) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -943,23 +974,23 @@
lr1.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 90_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_75HzContent) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_75HzContent) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
@@ -967,14 +998,14 @@
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
lr.desiredRefreshRate = Fps::fromValue(fps);
- const auto mode = selector.getBestRefreshRate(layers, {});
+ const auto mode = selector.getBestFrameRateMode(layers, {});
EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses "
<< to_string(mode->getFps());
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_Multiples) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_Multiples) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -986,7 +1017,7 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
@@ -994,14 +1025,14 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30_Hz;
@@ -1009,18 +1040,18 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30_Hz;
lr1.name = "30Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
@@ -1031,28 +1062,28 @@
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
// The other layer starts to provide buffers
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1061,49 +1092,70 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) {
+TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) {
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
-
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
RefreshRateOrder::Descending);
- const std::array expectedRefreshRates = {kMode90, kMode60, kMode30};
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) {
+TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) {
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
RefreshRateOrder::Ascending);
- const std::array expectedRefreshRates = {kMode30, kMode60, kMode90};
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
+TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72);
+ auto selector = createSelector(kModes_30_60_90, kModeId72);
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
@@ -1111,20 +1163,31 @@
const auto refreshRates =
selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending);
- const std::array expectedRefreshRates = {kMode30, kMode60, kMode90};
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, kMode30}, {45_Hz, kMode90}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
+TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
// The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
// different group.
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72);
+ auto selector = createSelector(kModes_30_60_90, kModeId72);
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
@@ -1132,29 +1195,52 @@
const auto refreshRates = selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt,
RefreshRateOrder::Descending);
- const std::array expectedRefreshRates = {kMode90, kMode60, kMode30};
+ const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest, powerOnImminentConsidered) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
- auto [refreshRates, signals] = selector.getRankedRefreshRates({}, {});
+ auto [refreshRates, signals] = selector.getRankedFrameRates({}, {});
EXPECT_FALSE(signals.powerOnImminent);
- std::array expectedRefreshRates = {kMode90, kMode60};
+ auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90},
+ {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
std::tie(refreshRates, signals) =
@@ -1164,9 +1250,11 @@
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1182,29 +1270,43 @@
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
std::tie(refreshRates, signals) =
selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false});
EXPECT_FALSE(signals.powerOnImminent);
- expectedRefreshRates = {kMode60, kMode90};
+ expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{60_Hz, kMode60}, {90_Hz, kMode90}};
+ case Config::FrameRateOverride::Enabled:
+ return {{60_Hz, kMode60}, {90_Hz, kMode90}, {45_Hz, kMode90},
+ {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
+ }
+ }();
ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
- EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].modePtr)
- << "Expected fps " << expectedRefreshRates[i]->getFps().getIntValue()
- << " Actual fps " << refreshRates[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+ << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+ << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+ << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest, touchConsidered) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, touchConsidered) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
- auto [_, signals] = selector.getRankedRefreshRates({}, {});
+ auto [_, signals] = selector.getRankedFrameRates({}, {});
EXPECT_FALSE(signals.touch);
std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true});
@@ -1251,8 +1353,8 @@
EXPECT_FALSE(signals.touch);
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitDefault) {
- TestableRefreshRateSelector selector(kModes_60_90_72_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitDefault) {
+ auto selector = createSelector(kModes_60_90_72_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
@@ -1284,57 +1386,57 @@
ss << "ExplicitDefault " << desired;
lr.name = ss.str();
- EXPECT_EQ(expected, selector.getBestRefreshRate(layers)->getFps());
+ EXPECT_EQ(expected, selector.getBestFrameRateMode(layers)->getFps());
}
}
-TEST_F(RefreshRateSelectorTest,
- getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
// Test that 23.976 will choose 24 if 23.976 is not supported
{
- TestableRefreshRateSelector selector(makeModes(kMode24, kMode25, kMode30, kMode30Frac,
- kMode60, kMode60Frac),
- kModeId60);
+ auto selector = createSelector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, kMode60,
+ kMode60Frac),
+ kModeId60);
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.desiredRefreshRate = 23.976_Hz;
lr.name = "ExplicitExactOrMultiple 23.976 Hz";
- EXPECT_EQ(kModeId24, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId24, selector.getBestFrameRateMode(layers)->getId());
}
// Test that 24 will choose 23.976 if 24 is not supported
{
- TestableRefreshRateSelector selector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
- kMode60, kMode60Frac),
- kModeId60);
+ auto selector = createSelector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
+ kMode60, kMode60Frac),
+ kModeId60);
lr.desiredRefreshRate = 24_Hz;
lr.name = "ExplicitExactOrMultiple 24 Hz";
- EXPECT_EQ(kModeId24Frac, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId24Frac, selector.getBestFrameRateMode(layers)->getId());
}
// Test that 29.97 will prefer 59.94 over 60 and 30
{
- TestableRefreshRateSelector selector(makeModes(kMode24, kMode24Frac, kMode25, kMode30,
- kMode60, kMode60Frac),
- kModeId60);
+ auto selector = createSelector(makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode60,
+ kMode60Frac),
+ kModeId60);
lr.desiredRefreshRate = 29.97_Hz;
lr.name = "ExplicitExactOrMultiple 29.97 Hz";
- EXPECT_EQ(kModeId60Frac, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId());
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) {
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact_WithFractionalRefreshRates) {
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
// Test that voting for supported refresh rate will select this refresh rate
{
- TestableRefreshRateSelector selector(kModes_24_25_30_50_60_Frac, kModeId60);
+ auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60);
for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) {
lr.vote = LayerVoteType::ExplicitExact;
@@ -1343,14 +1445,14 @@
ss << "ExplicitExact " << desired;
lr.name = ss.str();
- EXPECT_EQ(lr.desiredRefreshRate, selector.getBestRefreshRate(layers)->getFps());
+ EXPECT_EQ(lr.desiredRefreshRate, selector.getBestFrameRateMode(layers)->getFps());
}
}
}
-TEST_F(RefreshRateSelectorTest,
- getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
+ auto selector = createSelector(kModes_60_90, kModeId90);
constexpr FpsRange k90 = {90_Hz, 90_Hz};
constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
@@ -1365,16 +1467,16 @@
lr.name = "60Hz ExplicitDefault";
lr.focused = true;
- const auto [mode, signals] =
- selector.getRankedRefreshRates(layers, {.touch = true, .idle = true});
+ const auto [rankedFrameRate, signals] =
+ selector.getRankedFrameRates(layers, {.touch = true, .idle = true});
- EXPECT_EQ(mode.begin()->modePtr, kMode60);
+ EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60);
EXPECT_FALSE(signals.touch);
}
-TEST_F(RefreshRateSelectorTest,
- getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
constexpr FpsRange k60 = {60_Hz, 60_Hz};
constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
@@ -1389,11 +1491,11 @@
lr.desiredRefreshRate = 90_Hz;
lr.name = "90Hz ExplicitDefault";
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers, {.idle = true}));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.idle = true}));
}
-TEST_F(RefreshRateSelectorTest, testDisplayModeOrdering) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f},
{.weight = 1.f},
@@ -1426,15 +1528,31 @@
lr5.name = "30Hz";
lr5.focused = true;
- std::array expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30};
- auto actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+ auto expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{120_Hz, kMode120},
+ {90_Hz, kMode90},
+ {72_Hz, kMode72},
+ {60_Hz, kMode60},
+ {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+ }
+ }();
+ auto actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
ASSERT_EQ(expectedRanking.size(), actualRanking.size());
for (size_t i = 0; i < expectedRanking.size(); ++i) {
- EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
- << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
- << actualRanking[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
lr1.vote = LayerVoteType::Max;
@@ -1452,15 +1570,31 @@
lr5.desiredRefreshRate = 120_Hz;
lr5.name = "120Hz";
- expectedRanking = {kMode120, kMode90, kMode72, kMode60, kMode30};
- actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+ expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{120_Hz, kMode120},
+ {90_Hz, kMode90},
+ {72_Hz, kMode72},
+ {60_Hz, kMode60},
+ {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{120_Hz, kMode120}, {90_Hz, kMode90}, {72_Hz, kMode72}, {60_Hz, kMode60},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+ }
+ }();
+ actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
ASSERT_EQ(expectedRanking.size(), actualRanking.size());
for (size_t i = 0; i < expectedRanking.size(); ++i) {
- EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
- << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
- << actualRanking[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
lr1.vote = LayerVoteType::Heuristic;
@@ -1476,15 +1610,31 @@
lr5.desiredRefreshRate = 72_Hz;
lr5.name = "72Hz";
- expectedRanking = {kMode30, kMode60, kMode90, kMode120, kMode72};
- actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+ expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, kMode30},
+ {60_Hz, kMode60},
+ {90_Hz, kMode90},
+ {120_Hz, kMode120},
+ {72_Hz, kMode72}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}, {120_Hz, kMode120},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {72_Hz, kMode72}, {36_Hz, kMode72}};
+ }
+ }();
+ actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
ASSERT_EQ(expectedRanking.size(), actualRanking.size());
for (size_t i = 0; i < expectedRanking.size(); ++i) {
- EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
- << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
- << actualRanking[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
lr1.desiredRefreshRate = 120_Hz;
@@ -1503,21 +1653,37 @@
lr5.desiredRefreshRate = 120_Hz;
lr5.name = "120Hz-2";
- expectedRanking = {kMode90, kMode60, kMode120, kMode72, kMode30};
- actualRanking = selector.getRankedRefreshRates(layers, {}).ranking;
+ expectedRanking = []() -> std::vector<FrameRateMode> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{90_Hz, kMode90},
+ {60_Hz, kMode60},
+ {120_Hz, kMode120},
+ {72_Hz, kMode72},
+ {30_Hz, kMode30}};
+ case Config::FrameRateOverride::Enabled:
+ return {{90_Hz, kMode90}, {60_Hz, kMode60}, {120_Hz, kMode120}, {72_Hz, kMode72},
+ {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+ }
+ }();
+ actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
ASSERT_EQ(expectedRanking.size(), actualRanking.size());
for (size_t i = 0; i < expectedRanking.size(); ++i) {
- EXPECT_EQ(expectedRanking[i], actualRanking[i].modePtr)
- << "Expected fps " << expectedRanking[i]->getFps().getIntValue() << " Actual fps "
- << actualRanking[i].modePtr->getFps().getIntValue();
+ EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+ << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+ << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+ << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+ << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
}
}
-TEST_F(RefreshRateSelectorTest,
- getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
+ auto selector = createSelector(kModes_60_90, kModeId90);
constexpr FpsRange k90 = {90_Hz, 90_Hz};
constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
@@ -1525,8 +1691,8 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
- const auto [ranking, signals] = selector.getRankedRefreshRates({}, {});
- EXPECT_EQ(ranking.front().modePtr, kMode90);
+ const auto [ranking, signals] = selector.getRankedFrameRates({}, {});
+ EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90);
EXPECT_FALSE(signals.touch);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1536,50 +1702,50 @@
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz ExplicitExactOrMultiple";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::ExplicitDefault;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz ExplicitDefault";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.focused = true;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Heuristic;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Max;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Max";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.vote = LayerVoteType::Min;
lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Min";
lr.focused = false;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
lr.focused = true;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingNotAllowed) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingNotAllowed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
// The default policy doesn't allow group switching. Verify that no
// group switches are performed.
@@ -1591,11 +1757,11 @@
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayer) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayer) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1609,11 +1775,11 @@
layer.seamlessness = Seamlessness::SeamedAndSeamless;
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1628,11 +1794,11 @@
layer.seamlessness = Seamlessness::OnlySeamless;
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1649,11 +1815,11 @@
layer.seamlessness = Seamlessness::OnlySeamless;
layer.name = "60Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1673,11 +1839,11 @@
layer.name = "60Hz ExplicitDefault";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1702,11 +1868,11 @@
layers[1].name = "90Hz ExplicitDefault";
layers[1].focused = false;
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1735,11 +1901,11 @@
layers[1].vote = LayerVoteType::ExplicitDefault;
layers[1].name = "90Hz ExplicitDefault";
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId60);
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId60);
RefreshRateSelector::DisplayManagerPolicy policy;
policy.defaultMode = selector.getCurrentPolicy().defaultMode;
@@ -1765,11 +1931,11 @@
layers[1].vote = LayerVoteType::ExplicitDefault;
layers[1].name = "90Hz ExplicitDefault";
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) {
- TestableRefreshRateSelector selector(kModes_30_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) {
+ auto selector = createSelector(kModes_30_60, kModeId60);
// Allow group switching.
RefreshRateSelector::DisplayManagerPolicy policy;
@@ -1785,14 +1951,14 @@
layer.name = "60Hz ExplicitExactOrMultiple";
layer.focused = true;
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
selector.setActiveModeId(kModeId120);
- EXPECT_EQ(kModeId120, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId120, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) {
- TestableRefreshRateSelector selector(kModes_25_30_50_60, kModeId60);
+TEST_P(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) {
+ auto selector = createSelector(kModes_25_30_50_60, kModeId60);
// Allow group switching.
RefreshRateSelector::DisplayManagerPolicy policy;
@@ -1813,18 +1979,18 @@
.weight = 1.f,
.focused = true}};
- EXPECT_EQ(kModeId50, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId50, selector.getBestFrameRateMode(layers)->getId());
auto& seamedLayer = layers[0];
seamedLayer.desiredRefreshRate = 30_Hz;
seamedLayer.name = "30Hz ExplicitDefault";
selector.setActiveModeId(kModeId30);
- EXPECT_EQ(kModeId25, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId25, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) {
- TestableRefreshRateSelector selector(kModes_60_90_G1, kModeId90);
+TEST_P(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) {
+ auto selector = createSelector(kModes_60_90_G1, kModeId90);
// Allow group switching.
RefreshRateSelector::DisplayManagerPolicy policy;
@@ -1835,11 +2001,11 @@
std::vector<LayerRequirement> layers = {
{.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate(layers)->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
}
-TEST_F(RefreshRateSelectorTest, primaryVsAppRequestPolicy) {
- TestableRefreshRateSelector selector(kModes_30_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, primaryVsAppRequestPolicy) {
+ auto selector = createSelector(kModes_30_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].name = "Test layer";
@@ -1849,13 +2015,14 @@
bool focused = true;
};
- // Returns the mode selected by getBestRefreshRate for a single layer with the given arguments.
+ // Returns the mode selected by getBestFrameRateMode for a single layer with the given
+ // arguments.
const auto getFrameRate = [&](LayerVoteType voteType, Fps fps,
Args args = {}) -> DisplayModeId {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = fps;
layers[0].focused = args.focused;
- return selector.getBestRefreshRate(layers, {.touch = args.touch})->getId();
+ return selector.getBestFrameRateMode(layers, {.touch = args.touch})->getId();
};
constexpr FpsRange k30_60 = {30_Hz, 60_Hz};
@@ -1864,7 +2031,7 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {k30_60, k30_60}, {k30_90, k30_90}}));
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate()->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode()->getId());
EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz));
EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
@@ -1897,22 +2064,23 @@
EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
}
-TEST_F(RefreshRateSelectorTest, idle) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, idle) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].name = "Test layer";
- const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId {
+ const auto getIdleDisplayModeId = [&](LayerVoteType voteType,
+ bool touchActive) -> DisplayModeId {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = 90_Hz;
const auto [ranking, signals] =
- selector.getRankedRefreshRates(layers, {.touch = touchActive, .idle = true});
+ selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
// Refresh rate will be chosen by either touch state or idle state.
EXPECT_EQ(!touchActive, signals.idle);
- return ranking.front().modePtr->getId();
+ return ranking.front().frameRateMode.modePtr->getId();
};
EXPECT_EQ(SetPolicyResult::Changed,
@@ -1921,38 +2089,38 @@
// Idle should be lower priority than touch boost.
{
constexpr bool kTouchActive = true;
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Min, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Max, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive));
- EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+ EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
EXPECT_EQ(kModeId90,
- getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+ getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
}
// With no layers, idle should still be lower priority than touch boost.
- EXPECT_EQ(kModeId90, selector.getBestRefreshRate({}, {.touch = true, .idle = true})->getId());
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
// Idle should be higher precedence than other layer frame rate considerations.
selector.setActiveModeId(kModeId90);
{
constexpr bool kTouchActive = false;
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Min, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Max, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive));
- EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+ EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
EXPECT_EQ(kModeId60,
- getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+ getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
}
// Idle should be applied rather than the active mode when there are no layers.
- EXPECT_EQ(kModeId60, selector.getBestRefreshRate({}, {.idle = true})->getId());
+ EXPECT_EQ(kModeId60, selector.getBestFrameRateMode({}, {.idle = true})->getId());
}
-TEST_F(RefreshRateSelectorTest, findClosestKnownFrameRate) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, findClosestKnownFrameRate) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
const auto knownFrameRate = selector.findClosestKnownFrameRate(Fps::fromValue(fps));
@@ -1969,8 +2137,8 @@
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_KnownFrameRate) {
- TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_KnownFrameRate) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
struct Expectation {
Fps fps;
@@ -1997,101 +2165,80 @@
for (const auto& [fps, mode] : knownFrameRatesExpectations) {
layer.desiredRefreshRate = fps;
- EXPECT_EQ(mode, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(mode, selector.getBestFrameRateMode(layers));
}
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExact) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
auto& explicitExactLayer = layers[0];
auto& explicitExactOrMultipleLayer = layers[1];
- explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
- explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
- explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
-
explicitExactLayer.vote = LayerVoteType::ExplicitExact;
explicitExactLayer.name = "ExplicitExact";
explicitExactLayer.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode30, selector.getBestRefreshRate(layers, {.touch = true}));
-
- explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
- explicitExactLayer.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 72_Hz;
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 90_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
-
- explicitExactLayer.desiredRefreshRate = 120_Hz;
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60,
- {.enableFrameRateOverride =
- Config::FrameRateOverride::Enabled});
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
- auto& explicitExactLayer = layers[0];
- auto& explicitExactOrMultipleLayer = layers[1];
-
explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
- explicitExactLayer.vote = LayerVoteType::ExplicitExact;
- explicitExactLayer.name = "ExplicitExact";
- explicitExactLayer.desiredRefreshRate = 30_Hz;
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ EXPECT_SCORED_FRAME_RATE(kMode30, 30_Hz, selector.getBestScoredFrameRate(layers));
+ EXPECT_SCORED_FRAME_RATE(kMode30, 30_Hz,
+ selector.getBestScoredFrameRate(layers, {.touch = true}));
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true}));
+ } else {
+ EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers));
+ EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz,
+ selector.getBestScoredFrameRate(layers, {.touch = true}));
+ }
explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
explicitExactLayer.desiredRefreshRate = 60_Hz;
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers));
+ } else {
+ EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers));
+ }
explicitExactLayer.desiredRefreshRate = 72_Hz;
- EXPECT_EQ(kMode72, selector.getBestRefreshRate(layers));
+ EXPECT_SCORED_FRAME_RATE(kMode72, 72_Hz, selector.getBestScoredFrameRate(layers));
explicitExactLayer.desiredRefreshRate = 90_Hz;
- EXPECT_EQ(kMode90, selector.getBestRefreshRate(layers));
+ EXPECT_SCORED_FRAME_RATE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers));
explicitExactLayer.desiredRefreshRate = 120_Hz;
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers));
+ EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ReadsCache) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ReadsCache) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
using GlobalSignals = RefreshRateSelector::GlobalSignals;
const auto args = std::make_pair(std::vector<LayerRequirement>{},
GlobalSignals{.touch = true, .idle = true});
- const RefreshRateSelector::RankedRefreshRates result = {{RefreshRateSelector::ScoredRefreshRate{
- kMode90}},
- {.touch = true}};
+ const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{
+ {90_Hz, kMode90}}},
+ GlobalSignals{.touch = true}};
selector.mutableGetRankedRefreshRatesCache() = {args, result};
- EXPECT_EQ(result, selector.getRankedRefreshRates(args.first, args.second));
+ EXPECT_EQ(result, selector.getRankedFrameRates(args.first, args.second));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_WritesCache) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId60);
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache());
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
- const auto result = selector.getRankedRefreshRates(layers, globalSignals);
+ const auto result = selector.getRankedFrameRates(layers, globalSignals);
const auto& cache = selector.mutableGetRankedRefreshRatesCache();
ASSERT_TRUE(cache);
@@ -2100,10 +2247,8 @@
EXPECT_EQ(cache->result, result);
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_ExplicitExactTouchBoost) {
- TestableRefreshRateSelector selector(kModes_60_120, kModeId60,
- {.enableFrameRateOverride =
- Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExactTouchBoost) {
+ auto selector = createSelector(kModes_60_120, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
auto& explicitExactLayer = layers[0];
@@ -2117,19 +2262,21 @@
explicitExactLayer.name = "ExplicitExact";
explicitExactLayer.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode120, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
+ } else {
+ EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers, {.touch = true}));
+ }
explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers, {.touch = true}));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
}
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
- TestableRefreshRateSelector selector(kModes_24_25_30_50_60_Frac, kModeId60,
- {.enableFrameRateOverride =
- Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_FractionalRefreshRates_ExactAndDefault) {
+ auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60);
std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}};
auto& explicitDefaultLayer = layers[0];
@@ -2143,12 +2290,12 @@
explicitDefaultLayer.name = "ExplicitDefault";
explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
- EXPECT_EQ(kMode60, selector.getBestRefreshRate(layers));
+ EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
}
// b/190578904
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_withCloseRefreshRates) {
- constexpr int kMinRefreshRate = 10;
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withCloseRefreshRates) {
+ const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
constexpr int kMaxRefreshRate = 240;
DisplayModes displayModes;
@@ -2159,14 +2306,13 @@
Fps::fromValue(static_cast<float>(fps))));
}
- const TestableRefreshRateSelector selector(std::move(displayModes),
- DisplayModeId(kMinRefreshRate));
+ const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate));
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
layers[0].desiredRefreshRate = fps;
layers[0].vote = vote;
- EXPECT_EQ(fps.getIntValue(), selector.getBestRefreshRate(layers)->getFps().getIntValue())
+ EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue())
<< "Failed for " << ftl::enum_string(vote);
};
@@ -2180,7 +2326,7 @@
}
// b/190578904
-TEST_F(RefreshRateSelectorTest, getBestRefreshRate_conflictingVotes) {
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_conflictingVotes) {
constexpr DisplayModeId kActiveModeId{0};
DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz),
createDisplayMode(DisplayModeId(1), 53_Hz),
@@ -2188,7 +2334,7 @@
createDisplayMode(DisplayModeId(3), 60_Hz));
const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
- const TestableRefreshRateSelector selector(std::move(displayModes), kActiveModeId);
+ const auto selector = createSelector(std::move(displayModes), kActiveModeId);
const std::vector<LayerRequirement> layers = {
{
@@ -2205,19 +2351,19 @@
},
};
- EXPECT_EQ(53_Hz, selector.getBestRefreshRate(layers, globalSignals)->getFps());
+ EXPECT_EQ(53_Hz, selector.getBestFrameRateMode(layers, globalSignals)->getFps());
}
-TEST_F(RefreshRateSelectorTest, modeComparison) {
+TEST_P(RefreshRateSelectorTest, modeComparison) {
EXPECT_LT(kMode60->getFps(), kMode90->getFps());
EXPECT_GE(kMode60->getFps(), kMode60->getFps());
EXPECT_GE(kMode90->getFps(), kMode90->getFps());
}
-TEST_F(RefreshRateSelectorTest, testKernelIdleTimerAction) {
+TEST_P(RefreshRateSelectorTest, testKernelIdleTimerAction) {
using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
- TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
+ auto selector = createSelector(kModes_60_90, kModeId90);
EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
@@ -2234,10 +2380,10 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
}
-TEST_F(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) {
+TEST_P(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) {
using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
- TestableRefreshRateSelector selector(kModes_60_120, kModeId120);
+ auto selector = createSelector(kModes_60_120, kModeId120);
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}));
@@ -2256,8 +2402,8 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
}
-TEST_F(RefreshRateSelectorTest, getFrameRateDivisor) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId30);
+TEST_P(RefreshRateSelectorTest, getFrameRateDivisor) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId30);
const auto frameRate = 30_Hz;
Fps displayRefreshRate = selector.getActiveMode().getFps();
@@ -2289,7 +2435,7 @@
EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(60_Hz, 59.94_Hz));
}
-TEST_F(RefreshRateSelectorTest, isFractionalPairOrMultiple) {
+TEST_P(RefreshRateSelectorTest, isFractionalPairOrMultiple) {
EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(23.976_Hz, 24_Hz));
EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 23.976_Hz));
@@ -2315,22 +2461,72 @@
EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
- RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120);
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
EXPECT_TRUE(selector.getFrameRateOverrides({}, 120_Hz, {}).empty());
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_60on120) {
- RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
- {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_NonExplicit) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].name = "Test layer";
layers[0].ownerUid = 1234;
layers[0].desiredRefreshRate = 60_Hz;
- layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].vote = LayerVoteType::NoVote;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::Min;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::Max;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::Heuristic;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_Disabled) {
+ if (GetParam() != Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = 60_Hz;
+
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+ layers[0].vote = LayerVoteType::ExplicitExact;
+ EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_60on120) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = 60_Hz;
+
+ layers[0].vote = LayerVoteType::ExplicitDefault;
auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
EXPECT_EQ(1u, frameRateOverrides.size());
ASSERT_EQ(1u, frameRateOverrides.count(1234));
@@ -2342,26 +2538,23 @@
ASSERT_EQ(1u, frameRateOverrides.count(1234));
EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
- layers[0].vote = LayerVoteType::NoVote;
+ layers[0].vote = LayerVoteType::ExplicitExact;
frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Min;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Max;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Heuristic;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
+ EXPECT_EQ(1u, frameRateOverrides.size());
+ ASSERT_EQ(1u, frameRateOverrides.count(1234));
+ EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) {
- RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
- {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
{.ownerUid = 5678, .weight = 1.f}};
@@ -2392,9 +2585,16 @@
EXPECT_TRUE(frameRateOverrides.empty());
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_touch) {
- RefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
- {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_touch) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}};
layers[0].name = "Test layer";
@@ -2432,88 +2632,87 @@
EXPECT_TRUE(frameRateOverrides.empty());
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate_Enabled) {
- RefreshRateSelector selector(kModes_60_120, kModeId120,
- {.enableFrameRateOverride = Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate) {
+ if (GetParam() == Config::FrameRateOverride::Disabled) {
+ return;
+ }
+
+ ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+ GetParam() == Config::FrameRateOverride::AppOverride ||
+ GetParam() == Config::FrameRateOverride::Enabled);
+
+ auto selector = createSelector(kModes_60_120, kModeId120);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].name = "Test layer";
layers[0].ownerUid = 1234;
layers[0].desiredRefreshRate = 30_Hz;
- layers[0].vote = LayerVoteType::ExplicitDefault;
+ const auto expetedFps =
+ GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ? 60_Hz : 30_Hz;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
EXPECT_EQ(1u, frameRateOverrides.size());
ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
+ EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
EXPECT_EQ(1u, frameRateOverrides.size());
ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
+ EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
- layers[0].vote = LayerVoteType::NoVote;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Min;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Max;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Heuristic;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-}
-
-TEST_F(RefreshRateSelectorTest,
- getFrameRateOverrides_DivisorIsNotDisplayRefreshRate_EnabledForNativeRefreshRates) {
- RefreshRateSelector selector(kModes_60_120, kModeId120,
- {.enableFrameRateOverride =
- Config::FrameRateOverride::EnabledForNativeRefreshRates});
-
- std::vector<LayerRequirement> layers = {{.weight = 1.f}};
- layers[0].name = "Test layer";
- layers[0].ownerUid = 1234;
- layers[0].desiredRefreshRate = 30_Hz;
- layers[0].vote = LayerVoteType::ExplicitDefault;
-
- auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_EQ(1u, frameRateOverrides.size());
- ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[0].vote = LayerVoteType::ExplicitExact;
frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
EXPECT_EQ(1u, frameRateOverrides.size());
ASSERT_EQ(1u, frameRateOverrides.count(1234));
- EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
- layers[0].vote = LayerVoteType::NoVote;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Min;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Max;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
-
- layers[0].vote = LayerVoteType::Heuristic;
- frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
- EXPECT_TRUE(frameRateOverrides.empty());
+ EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
}
-TEST_F(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) {
- TestableRefreshRateSelector selector(kModes_30_60_72_90_120, kModeId120,
- {.enableFrameRateOverride =
- Config::FrameRateOverride::Enabled});
+TEST_P(RefreshRateSelectorTest, renderFrameRateInvalidPolicy) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ // The render frame rate cannot be greater than the physical refresh rate
+ {
+ const FpsRange physical = {60_Hz, 60_Hz};
+ const FpsRange render = {60_Hz, 120_Hz};
+ EXPECT_EQ(SetPolicyResult::Invalid,
+ selector.setDisplayManagerPolicy(
+ {kModeId60, {physical, render}, {physical, render}}));
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRateRestrictsPhysicalRefreshRate) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ {
+ const FpsRange physical = {0_Hz, 120_Hz};
+ const FpsRange render = {0_Hz, 60_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId60, {physical, render}, {physical, render}}));
+ const auto expectedMaxMode =
+ GetParam() == Config::FrameRateOverride::Enabled ? kMode120 : kMode60;
+ EXPECT_EQ(expectedMaxMode, selector.getMaxRefreshRateByPolicy());
+ EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy());
+ }
+
+ {
+ const FpsRange physical = {0_Hz, 120_Hz};
+ const FpsRange render = {120_Hz, 120_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId60, {physical, render}, {physical, render}}));
+ EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy());
+ EXPECT_EQ(kMode120, selector.getMinRefreshRateByPolicy());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
std::vector<LayerRequirement> layers = {{.weight = 1.f}};
{
@@ -2566,5 +2765,153 @@
EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
}
+TEST_P(RefreshRateSelectorTest, renderFrameRates) {
+ auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+ // [renderRate, refreshRate]
+ const auto expected = []() -> std::vector<std::pair<Fps, Fps>> {
+ switch (GetParam()) {
+ case Config::FrameRateOverride::Disabled:
+ case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+ case Config::FrameRateOverride::AppOverride:
+ return {{30_Hz, 30_Hz},
+ {60_Hz, 60_Hz},
+ {72_Hz, 72_Hz},
+ {90_Hz, 90_Hz},
+ {120_Hz, 120_Hz}};
+ case Config::FrameRateOverride::Enabled:
+ return {{30_Hz, 30_Hz}, {36_Hz, 72_Hz}, {40_Hz, 120_Hz}, {45_Hz, 90_Hz},
+ {60_Hz, 60_Hz}, {72_Hz, 72_Hz}, {90_Hz, 90_Hz}, {120_Hz, 120_Hz}};
+ }
+ }();
+
+ const auto& primaryRefreshRates = selector.getPrimaryFrameRates();
+ ASSERT_EQ(expected.size(), primaryRefreshRates.size());
+
+ for (size_t i = 0; i < expected.size(); i++) {
+ const auto [expectedRenderRate, expectedRefreshRate] = expected[i];
+ EXPECT_EQ(expectedRenderRate, primaryRefreshRates[i].fps);
+ EXPECT_EQ(expectedRefreshRate, primaryRefreshRates[i].modePtr->getFps());
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, refreshRateIsCappedWithRenderFrameRate) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ auto selector = createSelector(kModes_60_120, kModeId60);
+
+ constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz};
+ constexpr FpsRange k0_60Hz = {0_Hz, 60_Hz};
+
+ constexpr FpsRanges kAppRequest = {/*physical*/ k0_120Hz,
+ /*render*/ k0_120Hz};
+
+ EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate());
+ {
+ constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
+ /*render*/ k0_120Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+ /*primaryRanges*/
+ kPrimary,
+ /*appRequestRanges*/
+ kAppRequest}));
+ }
+ EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate());
+
+ {
+ constexpr FpsRanges kPrimary = {/*physical*/ k0_60Hz,
+ /*render*/ k0_60Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+ /*primaryRanges*/
+ kPrimary,
+ /*appRequestRanges*/
+ kAppRequest}));
+ }
+ EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate());
+
+ {
+ constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
+ /*render*/ k0_60Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+ /*primaryRanges*/
+ kPrimary,
+ /*appRequestRanges*/
+ kAppRequest}));
+ }
+ EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate());
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRates_60_120) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& layer = layers[0];
+
+ const auto expectedRenderRate =
+ GetParam() == Config::FrameRateOverride::Enabled ? 30_Hz : 60_Hz;
+
+ layer.name = "30Hz ExplicitDefault";
+ layer.desiredRefreshRate = 30_Hz;
+ layer.vote = LayerVoteType::ExplicitDefault;
+ EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+
+ layer.name = "30Hz Heuristic";
+ layer.desiredRefreshRate = 30_Hz;
+ layer.vote = LayerVoteType::Heuristic;
+ EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+
+ layer.name = "30Hz ExplicitExactOrMultiple";
+ layer.desiredRefreshRate = 30_Hz;
+ layer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, idleWhenLowestRefreshRateIsNotDivisor) {
+ auto selector = createSelector(kModes_35_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+
+ const auto getIdleDisplayModeId = [&](LayerVoteType voteType,
+ bool touchActive) -> DisplayModeId {
+ layers[0].vote = voteType;
+ layers[0].desiredRefreshRate = 90_Hz;
+
+ const auto [ranking, signals] =
+ selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
+
+ // Refresh rate will be chosen by either touch state or idle state.
+ EXPECT_EQ(!touchActive, signals.idle);
+ return ranking.front().frameRateMode.modePtr->getId();
+ };
+
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 90_Hz}}));
+
+ // With no layers, idle should still be lower priority than touch boost.
+ EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
+
+ // Idle should be higher precedence than other layer frame rate considerations.
+ selector.setActiveModeId(kModeId90);
+ {
+ constexpr bool kTouchActive = false;
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+ EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
+ EXPECT_EQ(kModeId35,
+ getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+ }
+
+ // Idle should be applied rather than the active mode when there are no layers.
+ EXPECT_EQ(kModeId35, selector.getBestFrameRateMode({}, {.idle = true})->getId());
+}
+
} // namespace
} // namespace android::scheduler