Merge "SF: Add workaround to release screenshot buffer" into pi-dev
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 70164ea..fe9dd56 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -58,6 +58,7 @@
#define MAX_SYS_FILES 10
const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
+const char* k_userInitiatedTraceProperty = "debug.atrace.user_initiated";
const char* k_traceAppsNumberProperty = "debug.atrace.app_number";
const char* k_traceAppsPropertyTemplate = "debug.atrace.app_%d";
@@ -186,7 +187,10 @@
{ REQ, "events/cpufreq_interactive/enable" },
} },
{ "sync", "Synchronization", 0, {
- { REQ, "events/sync/enable" },
+ // before linux kernel 4.9
+ { OPT, "events/sync/enable" },
+ // starting in linux kernel 4.9
+ { OPT, "events/fence/enable" },
} },
{ "workq", "Kernel Workqueues", 0, {
{ REQ, "events/workqueue/enable" },
@@ -444,6 +448,16 @@
return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable);
}
+// Set the user initiated trace property
+static bool setUserInitiatedTraceProperty(bool enable)
+{
+ if (!android::base::SetProperty(k_userInitiatedTraceProperty, enable ? "1" : "")) {
+ fprintf(stderr, "error setting user initiated strace system property\n");
+ return false;
+ }
+ return true;
+}
+
// Enable or disable kernel tracing.
static bool setTracingEnabled(bool enable)
{
@@ -837,6 +851,8 @@
{
bool ok = true;
+ ok &= setUserInitiatedTraceProperty(true);
+
// Set up the tracing options.
ok &= setCategoriesEnableFromFile(g_categoriesFile);
ok &= setTraceOverwriteEnable(g_traceOverwrite);
@@ -884,6 +900,7 @@
setTraceBufferSizeKB(1);
setPrintTgidEnableIfPresent(false);
setKernelTraceFuncs(NULL);
+ setUserInitiatedTraceProperty(false);
}
// Enable tracing in the kernel.
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index a9c5c82..d3d0711 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -83,6 +83,11 @@
chmod 0666 /sys/kernel/tracing/events/i2c/smbus_reply/enable
chmod 0666 /sys/kernel/debug/tracing/events/lowmemorykiller/enable
chmod 0666 /sys/kernel/tracing/events/lowmemorykiller/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/sync/enable
+ chmod 0666 /sys/kernel/tracing/events/sync/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/fence/enable
+ chmod 0666 /sys/kernel/tracing/events/fence/enable
+
# disk
chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_enter/enable
diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc
index 93a29ba..f4e5b98 100644
--- a/cmds/atrace/atrace_userdebug.rc
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -5,8 +5,6 @@
# Access control to these files is now entirely in selinux policy.
on post-fs
- chmod 0666 /sys/kernel/tracing/events/sync/enable
- chmod 0666 /sys/kernel/debug/tracing/events/sync/enable
chmod 0666 /sys/kernel/tracing/events/workqueue/enable
chmod 0666 /sys/kernel/debug/tracing/events/workqueue/enable
chmod 0666 /sys/kernel/tracing/events/regulator/enable
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 3e7d2a0..9d897f5 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1504,6 +1504,7 @@
// This method collects dumpsys for telephony debugging only
static void DumpstateTelephonyOnly() {
DurationReporter duration_reporter("DUMPSTATE");
+ const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
DumpstateRadioCommon();
@@ -1529,6 +1530,13 @@
RunDumpsys("TELEPHONY SERVICES", {"activity", "service", "TelephonyDebugService"});
printf("========================================================\n");
+ printf("== Running Application Services (non-platform)\n");
+ printf("========================================================\n");
+
+ RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS);
+
+ printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
}
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 624d7d6..9615a75 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -309,7 +309,9 @@
// If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
const char* dex2oat_bin = "/system/bin/dex2oat";
constexpr const char* kDex2oatDebugPath = "/system/bin/dex2oatd";
- if (is_debug_runtime() || (background_job_compile && is_debuggable_build())) {
+ // Do not use dex2oatd for release candidates (give dex2oat more soak time).
+ bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL";
+ if (is_debug_runtime() || (background_job_compile && is_debuggable_build() && !is_release)) {
if (access(kDex2oatDebugPath, X_OK) == 0) {
dex2oat_bin = kDex2oatDebugPath;
}
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 3ae56db..e90cf3b 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -115,7 +115,7 @@
exit(207);
}
{
- std::string vendor_partition = StringPrintf("/dev/block/bootdevice/by-name/vendor%s",
+ std::string vendor_partition = StringPrintf("/dev/block/by-name/vendor%s",
arg[2]);
int vendor_result = mount(vendor_partition.c_str(),
"/postinstall/vendor",
@@ -128,7 +128,7 @@
// Try to mount the product partition. update_engine doesn't do this for us, but we
// want it for product APKs. Same notes as vendor above.
{
- std::string product_partition = StringPrintf("/dev/block/bootdevice/by-name/product%s",
+ std::string product_partition = StringPrintf("/dev/block/by-name/product%s",
arg[2]);
int product_result = mount(product_partition.c_str(),
"/postinstall/product",
diff --git a/data/etc/aosp_excluded_hardware.xml b/data/etc/aosp_excluded_hardware.xml
new file mode 100644
index 0000000..013f278
--- /dev/null
+++ b/data/etc/aosp_excluded_hardware.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 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.
+-->
+
+<permissions>
+ <!-- This should be used to exclude this feature from aosp targets. As aosp configurations
+ may or may not have a valid location provider -->
+ <unavailable-feature name="android.hardware.location.network" />
+</permissions>
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 289433b..449a9e9 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -46,7 +46,7 @@
uint32_t BpBinder::sBinderProxyCountLowWatermark = 2000;
enum {
- CALLBACK_TRIGGERED_MASK = 0x80000000, // A flag denoting that the callback has been called
+ LIMIT_REACHED_MASK = 0x80000000, // A flag denoting that the limit has been reached
COUNTING_VALUE_MASK = 0x7FFFFFFF, // A mask of the remaining bits for the count value
};
@@ -109,35 +109,30 @@
BpBinder* BpBinder::create(int32_t handle) {
int32_t trackedUid = -1;
if (sCountByUidEnabled) {
- BpBinder* out;
trackedUid = IPCThreadState::self()->getCallingUid();
AutoMutex _l(sTrackingLock);
- if ((sTrackingMap[trackedUid] & COUNTING_VALUE_MASK) >= sBinderProxyCountHighWatermark) {
- ALOGE("Too many binder proxy objects sent to uid %d from uid %d (over %d proxies held)",
- getuid(), trackedUid, sBinderProxyCountHighWatermark);
-
+ uint32_t trackedValue = sTrackingMap[trackedUid];
+ if (CC_UNLIKELY(trackedValue & LIMIT_REACHED_MASK)) {
if (sBinderProxyThrottleCreate) {
- ALOGE("Returning Null Binder Proxy Object to uid %d", trackedUid);
- out = nullptr;
- } else {
- // increment and construct here in case callback has an async kill causing a race
- sTrackingMap[trackedUid]++;
- out = new BpBinder(handle, trackedUid);
- }
-
- if (sLimitCallback && !(sTrackingMap[trackedUid] & CALLBACK_TRIGGERED_MASK)) {
- sTrackingMap[trackedUid] |= CALLBACK_TRIGGERED_MASK;
- sLimitCallback(trackedUid);
+ return nullptr;
}
} else {
- sTrackingMap[trackedUid]++;
- out = new BpBinder(handle, trackedUid);
+ if ((trackedValue & COUNTING_VALUE_MASK) >= sBinderProxyCountHighWatermark) {
+ ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
+ getuid(), trackedUid, trackedValue);
+ sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK;
+ if (sLimitCallback) sLimitCallback(trackedUid);
+ if (sBinderProxyThrottleCreate) {
+ ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy"
+ " count drops below %d",
+ trackedUid, getuid(), sBinderProxyCountLowWatermark);
+ return nullptr;
+ }
+ }
}
-
- return out;
- } else {
- return new BpBinder(handle, trackedUid);
+ sTrackingMap[trackedUid]++;
}
+ return new BpBinder(handle, trackedUid);
}
BpBinder::BpBinder(int32_t handle, int32_t trackedUid)
@@ -150,7 +145,7 @@
ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);
extendObjectLifetime(OBJECT_LIFETIME_WEAK);
- IPCThreadState::self()->incWeakHandle(handle);
+ IPCThreadState::self()->incWeakHandle(handle, this);
}
bool BpBinder::isDescriptorCached() const {
@@ -371,15 +366,17 @@
if (mTrackedUid >= 0) {
AutoMutex _l(sTrackingLock);
- if (CC_UNLIKELY(sTrackingMap[mTrackedUid] == 0)) {
+ uint32_t trackedValue = sTrackingMap[mTrackedUid];
+ if (CC_UNLIKELY((trackedValue & COUNTING_VALUE_MASK) == 0)) {
ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this, mHandle);
} else {
if (CC_UNLIKELY(
- (sTrackingMap[mTrackedUid] & CALLBACK_TRIGGERED_MASK) &&
- ((sTrackingMap[mTrackedUid] & COUNTING_VALUE_MASK) <= sBinderProxyCountLowWatermark)
+ (trackedValue & LIMIT_REACHED_MASK) &&
+ ((trackedValue & COUNTING_VALUE_MASK) <= sBinderProxyCountLowWatermark)
)) {
- // Clear the Callback Triggered bit when crossing below the low watermark
- sTrackingMap[mTrackedUid] &= ~CALLBACK_TRIGGERED_MASK;
+ ALOGI("Limit reached bit reset for uid %d (fewer than %d proxies from uid %d held)",
+ getuid(), mTrackedUid, sBinderProxyCountLowWatermark);
+ sTrackingMap[mTrackedUid] &= ~LIMIT_REACHED_MASK;
}
if (--sTrackingMap[mTrackedUid] == 0) {
sTrackingMap.erase(mTrackedUid);
@@ -412,7 +409,7 @@
{
ALOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle);
IPCThreadState* ipc = IPCThreadState::self();
- if (ipc) ipc->incStrongHandle(mHandle);
+ if (ipc) ipc->incStrongHandle(mHandle, this);
}
void BpBinder::onLastStrongRef(const void* /*id*/)
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index ba9bf61..fd552b4 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -409,6 +409,15 @@
if (mProcess->mDriverFD <= 0)
return;
talkWithDriver(false);
+ // The flush could have caused post-write refcount decrements to have
+ // been executed, which in turn could result in BC_RELEASE/BC_DECREFS
+ // being queued in mOut. So flush again, if we need to.
+ if (mOut.dataSize() > 0) {
+ talkWithDriver(false);
+ }
+ if (mOut.dataSize() > 0) {
+ ALOGW("mOut.dataSize() > 0 after flushCommands()");
+ }
}
void IPCThreadState::blockUntilThreadAvailable()
@@ -501,6 +510,21 @@
}
}
+void IPCThreadState::processPostWriteDerefs()
+{
+ for (size_t i = 0; i < mPostWriteWeakDerefs.size(); i++) {
+ RefBase::weakref_type* refs = mPostWriteWeakDerefs[i];
+ refs->decWeak(mProcess.get());
+ }
+ mPostWriteWeakDerefs.clear();
+
+ for (size_t i = 0; i < mPostWriteStrongDerefs.size(); i++) {
+ RefBase* obj = mPostWriteStrongDerefs[i];
+ obj->decStrong(mProcess.get());
+ }
+ mPostWriteStrongDerefs.clear();
+}
+
void IPCThreadState::joinThreadPool(bool isMain)
{
LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());
@@ -627,11 +651,14 @@
return err;
}
-void IPCThreadState::incStrongHandle(int32_t handle)
+void IPCThreadState::incStrongHandle(int32_t handle, BpBinder *proxy)
{
LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle);
mOut.writeInt32(BC_ACQUIRE);
mOut.writeInt32(handle);
+ // Create a temp reference until the driver has handled this command.
+ proxy->incStrong(mProcess.get());
+ mPostWriteStrongDerefs.push(proxy);
}
void IPCThreadState::decStrongHandle(int32_t handle)
@@ -641,11 +668,14 @@
mOut.writeInt32(handle);
}
-void IPCThreadState::incWeakHandle(int32_t handle)
+void IPCThreadState::incWeakHandle(int32_t handle, BpBinder *proxy)
{
LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle);
mOut.writeInt32(BC_INCREFS);
mOut.writeInt32(handle);
+ // Create a temp reference until the driver has handled this command.
+ proxy->getWeakRefs()->incWeak(mProcess.get());
+ mPostWriteWeakDerefs.push(proxy->getWeakRefs());
}
void IPCThreadState::decWeakHandle(int32_t handle)
@@ -897,8 +927,10 @@
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
- else
+ else {
mOut.setDataSize(0);
+ processPostWriteDerefs();
+ }
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 245607e..c1d9a9a 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -64,9 +64,9 @@
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
- void incStrongHandle(int32_t handle);
+ void incStrongHandle(int32_t handle, BpBinder *proxy);
void decStrongHandle(int32_t handle);
- void incWeakHandle(int32_t handle);
+ void incWeakHandle(int32_t handle, BpBinder *proxy);
void decWeakHandle(int32_t handle);
status_t attemptIncStrongHandle(int32_t handle);
static void expungeHandle(int32_t handle, IBinder* binder);
@@ -106,6 +106,7 @@
status_t getAndExecuteCommand();
status_t executeCommand(int32_t command);
void processPendingDerefs();
+ void processPostWriteDerefs();
void clearCaller();
@@ -118,7 +119,8 @@
const sp<ProcessState> mProcess;
Vector<BBinder*> mPendingStrongDerefs;
Vector<RefBase::weakref_type*> mPendingWeakDerefs;
-
+ Vector<RefBase*> mPostWriteStrongDerefs;
+ Vector<RefBase::weakref_type*> mPostWriteWeakDerefs;
Parcel mIn;
Parcel mOut;
status_t mLastError;
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index 0183621..fedc789 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -80,46 +80,44 @@
#elif defined(__i386__)
- #define API_ENTRY(_api) __attribute__((noinline,optimize("omit-frame-pointer"))) _api
+ #define API_ENTRY(_api) __attribute__((naked)) _api
#define CALL_GL_EXTENSION_API(_api) \
- register void** fn; \
__asm__ volatile( \
- "mov %%gs:0, %[fn]\n" \
- "mov %P[tls](%[fn]), %[fn]\n" \
- "test %[fn], %[fn]\n" \
- "cmovne %P[api](%[fn]), %[fn]\n" \
- "test %[fn], %[fn]\n" \
+ "mov %%gs:0, %%eax\n" \
+ "mov %P[tls](%%eax), %%eax\n" \
+ "test %%eax, %%eax\n" \
+ "cmovne %P[api](%%eax), %%eax\n" \
+ "test %%eax, %%eax\n" \
"je 1f\n" \
- "jmp *%[fn]\n" \
- "1:\n" \
- : [fn] "=r" (fn) \
+ "jmp *%%eax\n" \
+ "1: ret\n" \
+ : \
: [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)), \
[api] "i" (__builtin_offsetof(gl_hooks_t, \
ext.extensions[_api])) \
- : "cc" \
+ : "eax", "cc" \
);
#elif defined(__x86_64__)
- #define API_ENTRY(_api) __attribute__((noinline,optimize("omit-frame-pointer"))) _api
+ #define API_ENTRY(_api) __attribute__((naked)) _api
#define CALL_GL_EXTENSION_API(_api) \
- register void** fn; \
__asm__ volatile( \
- "mov %%fs:0, %[fn]\n" \
- "mov %P[tls](%[fn]), %[fn]\n" \
- "test %[fn], %[fn]\n" \
- "cmovne %P[api](%[fn]), %[fn]\n" \
- "test %[fn], %[fn]\n" \
+ "mov %%fs:0, %%rax\n" \
+ "mov %P[tls](%%rax), %%rax\n" \
+ "test %%rax, %%rax\n" \
+ "cmovne %P[api](%%rax), %%rax\n" \
+ "test %%rax, %%rax\n" \
"je 1f\n" \
- "jmp *%[fn]\n" \
- "1:\n" \
- : [fn] "=r" (fn) \
+ "jmp *%%rax\n" \
+ "1: ret\n" \
+ : \
: [tls] "i" (TLS_SLOT_OPENGL_API*sizeof(void*)), \
[api] "i" (__builtin_offsetof(gl_hooks_t, \
ext.extensions[_api])) \
- : "cc" \
+ : "rax", "cc" \
);
#elif defined(__mips64)
diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp
index 62e9ce0..1025a88 100644
--- a/services/sensorservice/RecentEventLogger.cpp
+++ b/services/sensorservice/RecentEventLogger.cpp
@@ -100,7 +100,8 @@
size_t RecentEventLogger::logSizeBySensorType(int sensorType) {
return (sensorType == SENSOR_TYPE_STEP_COUNTER ||
sensorType == SENSOR_TYPE_SIGNIFICANT_MOTION ||
- sensorType == SENSOR_TYPE_ACCELEROMETER) ? LOG_SIZE_LARGE : LOG_SIZE;
+ sensorType == SENSOR_TYPE_ACCELEROMETER ||
+ sensorType == SENSOR_TYPE_LIGHT) ? LOG_SIZE_LARGE : LOG_SIZE;
}
RecentEventLogger::SensorEventLog::SensorEventLog(const sensors_event_t& e) : mEvent(e) {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 6a69844..c2bb6ad 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -22,6 +22,7 @@
"android.frameworks.vr.composer@1.0",
"android.hardware.configstore-utils",
"android.hardware.configstore@1.0",
+ "android.hardware.configstore@1.1",
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 6feec53..17ed9aa 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -553,7 +553,7 @@
default:
break;
}
- setDataSpace(dataSpace);
+ mCurrentDataSpace = dataSpace;
Rect crop(mConsumer->getCurrentCrop());
const uint32_t transform(mConsumer->getCurrentTransform());
@@ -660,10 +660,10 @@
setCompositionType(hwcId, HWC2::Composition::Device);
}
- ALOGV("setPerFrameData: dataspace = %d", mDrawingState.dataSpace);
- error = hwcLayer->setDataspace(mDrawingState.dataSpace);
+ ALOGV("setPerFrameData: dataspace = %d", mCurrentDataSpace);
+ error = hwcLayer->setDataspace(mCurrentDataSpace);
if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mDrawingState.dataSpace,
+ ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
to_string(error).c_str(), static_cast<int32_t>(error));
}
@@ -866,9 +866,9 @@
auto& engine(mFlinger->getRenderEngine());
engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), false /* disableTexture */,
getColor());
- engine.setSourceDataSpace(mCurrentState.dataSpace);
+ engine.setSourceDataSpace(mCurrentDataSpace);
- if (mCurrentState.dataSpace == ui::Dataspace::BT2020_ITU_PQ &&
+ if (mCurrentDataSpace == ui::Dataspace::BT2020_ITU_PQ &&
mConsumer->getCurrentApi() == NATIVE_WINDOW_API_MEDIA &&
getBE().compositionInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102) {
engine.setSourceY410BT2020(true);
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index c87b669..512564c 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -77,9 +77,9 @@
setCompositionType(hwcId, HWC2::Composition::SolidColor);
- error = hwcLayer->setDataspace(mDrawingState.dataSpace);
+ error = hwcLayer->setDataspace(mCurrentDataSpace);
if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mDrawingState.dataSpace,
+ ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
to_string(error).c_str(), static_cast<int32_t>(error));
}
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 2b1e577..9ecbacc 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -18,6 +18,9 @@
#undef LOG_TAG
#define LOG_TAG "DisplayDevice"
+#include <array>
+#include <unordered_set>
+
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -55,6 +58,7 @@
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
using android::ui::ColorMode;
+using android::ui::Dataspace;
using android::ui::Hdr;
using android::ui::RenderIntent;
@@ -65,6 +69,149 @@
uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0;
+namespace {
+
+// ordered list of known SDR color modes
+const std::array<ColorMode, 2> sSdrColorModes = {
+ ColorMode::DISPLAY_P3,
+ ColorMode::SRGB,
+};
+
+// ordered list of known HDR color modes
+const std::array<ColorMode, 2> sHdrColorModes = {
+ ColorMode::BT2100_PQ,
+ ColorMode::BT2100_HLG,
+};
+
+// ordered list of known SDR render intents
+const std::array<RenderIntent, 2> sSdrRenderIntents = {
+ RenderIntent::ENHANCE,
+ RenderIntent::COLORIMETRIC,
+};
+
+// ordered list of known HDR render intents
+const std::array<RenderIntent, 2> sHdrRenderIntents = {
+ RenderIntent::TONE_MAP_ENHANCE,
+ RenderIntent::TONE_MAP_COLORIMETRIC,
+};
+
+// map known color mode to dataspace
+Dataspace colorModeToDataspace(ColorMode mode) {
+ switch (mode) {
+ case ColorMode::SRGB:
+ return Dataspace::SRGB;
+ case ColorMode::DISPLAY_P3:
+ return Dataspace::DISPLAY_P3;
+ case ColorMode::BT2100_HLG:
+ return Dataspace::BT2020_HLG;
+ case ColorMode::BT2100_PQ:
+ return Dataspace::BT2020_PQ;
+ default:
+ return Dataspace::UNKNOWN;
+ }
+}
+
+// Return a list of candidate color modes.
+std::vector<ColorMode> getColorModeCandidates(ColorMode mode) {
+ std::vector<ColorMode> candidates;
+
+ // add mode itself
+ candidates.push_back(mode);
+
+ // check if mode is HDR
+ bool isHdr = false;
+ for (auto hdrMode : sHdrColorModes) {
+ if (hdrMode == mode) {
+ isHdr = true;
+ break;
+ }
+ }
+
+ // add other HDR candidates when mode is HDR
+ if (isHdr) {
+ for (auto hdrMode : sHdrColorModes) {
+ if (hdrMode != mode) {
+ candidates.push_back(hdrMode);
+ }
+ }
+ }
+
+ // add other SDR candidates
+ for (auto sdrMode : sSdrColorModes) {
+ if (sdrMode != mode) {
+ candidates.push_back(sdrMode);
+ }
+ }
+
+ return candidates;
+}
+
+// Return a list of candidate render intents.
+std::vector<RenderIntent> getRenderIntentCandidates(RenderIntent intent) {
+ std::vector<RenderIntent> candidates;
+
+ // add intent itself
+ candidates.push_back(intent);
+
+ // check if intent is HDR
+ bool isHdr = false;
+ for (auto hdrIntent : sHdrRenderIntents) {
+ if (hdrIntent == intent) {
+ isHdr = true;
+ break;
+ }
+ }
+
+ if (isHdr) {
+ // add other HDR candidates when intent is HDR
+ for (auto hdrIntent : sHdrRenderIntents) {
+ if (hdrIntent != intent) {
+ candidates.push_back(hdrIntent);
+ }
+ }
+ } else {
+ // add other SDR candidates when intent is SDR
+ for (auto sdrIntent : sSdrRenderIntents) {
+ if (sdrIntent != intent) {
+ candidates.push_back(sdrIntent);
+ }
+ }
+ }
+
+ return candidates;
+}
+
+// Return the best color mode supported by HWC.
+ColorMode getHwcColorMode(
+ const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
+ ColorMode mode) {
+ std::vector<ColorMode> candidates = getColorModeCandidates(mode);
+ for (auto candidate : candidates) {
+ auto iter = hwcColorModes.find(candidate);
+ if (iter != hwcColorModes.end()) {
+ return candidate;
+ }
+ }
+
+ return ColorMode::NATIVE;
+}
+
+// Return the best render intent supported by HWC.
+RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, RenderIntent intent) {
+ std::vector<RenderIntent> candidates = getRenderIntentCandidates(intent);
+ for (auto candidate : candidates) {
+ for (auto hwcIntent : hwcIntents) {
+ if (candidate == hwcIntent) {
+ return candidate;
+ }
+ }
+ }
+
+ return RenderIntent::COLORIMETRIC;
+}
+
+} // anonymous namespace
+
// clang-format off
DisplayDevice::DisplayDevice(
const sp<SurfaceFlinger>& flinger,
@@ -80,7 +227,7 @@
bool hasWideColorGamut,
const HdrCapabilities& hdrCapabilities,
const int32_t supportedPerFrameMetadata,
- const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hdrAndRenderIntents,
+ const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
int initialPowerMode)
: lastCompositionHadVisibleLayers(false),
mFlinger(flinger),
@@ -100,19 +247,16 @@
mFrame(Rect::INVALID_RECT),
mPowerMode(initialPowerMode),
mActiveConfig(0),
- mActiveColorMode(ColorMode::NATIVE),
mColorTransform(HAL_COLOR_TRANSFORM_IDENTITY),
mHasWideColorGamut(hasWideColorGamut),
mHasHdr10(false),
mHasHLG(false),
mHasDolbyVision(false),
- mSupportedPerFrameMetadata(supportedPerFrameMetadata),
- mHasBT2100PQColorimetric(false),
- mHasBT2100PQEnhance(false),
- mHasBT2100HLGColorimetric(false),
- mHasBT2100HLGEnhance(false)
+ mSupportedPerFrameMetadata(supportedPerFrameMetadata)
{
// clang-format on
+ populateColorModes(hwcColorModes);
+
std::vector<Hdr> types = hdrCapabilities.getSupportedHdrTypes();
for (Hdr hdrType : types) {
switch (hdrType) {
@@ -150,18 +294,6 @@
}
mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
- auto iter = hdrAndRenderIntents.find(ColorMode::BT2100_PQ);
- if (iter != hdrAndRenderIntents.end()) {
- hasToneMapping(iter->second,
- &mHasBT2100PQColorimetric, &mHasBT2100PQEnhance);
- }
-
- iter = hdrAndRenderIntents.find(ColorMode::BT2100_HLG);
- if (iter != hdrAndRenderIntents.end()) {
- hasToneMapping(iter->second,
- &mHasBT2100HLGColorimetric, &mHasBT2100HLGEnhance);
- }
-
// initialize the display orientation transform.
setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
}
@@ -475,6 +607,15 @@
TL.set(-src_x, -src_y);
TP.set(dst_x, dst_y);
+ // need to take care of primary display rotation for mGlobalTransform
+ // for case if the panel is not installed aligned with device orientation
+ if (mType == DisplayType::DISPLAY_PRIMARY) {
+ int primaryDisplayOrientation = mFlinger->getPrimaryDisplayOrientation();
+ DisplayDevice::orientationToTransfrom(
+ (orientation + primaryDisplayOrientation) % (DisplayState::eOrientation270 + 1),
+ w, h, &R);
+ }
+
// The viewport and frame are both in the logical orientation.
// Apply the logical translation, scale to physical size, apply the
// physical translation and finally rotate to the physical orientation.
@@ -546,20 +687,108 @@
result.append(surfaceDump);
}
-void DisplayDevice::hasToneMapping(const std::vector<RenderIntent>& renderIntents,
- bool* outColorimetric, bool *outEnhance) {
- for (auto intent : renderIntents) {
- switch (intent) {
- case RenderIntent::TONE_MAP_COLORIMETRIC:
- *outColorimetric = true;
- break;
- case RenderIntent::TONE_MAP_ENHANCE:
- *outEnhance = true;
- break;
- default:
- break;
+// Map dataspace/intent to the best matched dataspace/colorMode/renderIntent
+// supported by HWC.
+void DisplayDevice::addColorMode(
+ const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
+ const ColorMode mode, const RenderIntent intent) {
+ // find the best color mode
+ const ColorMode hwcColorMode = getHwcColorMode(hwcColorModes, mode);
+
+ // find the best render intent
+ auto iter = hwcColorModes.find(hwcColorMode);
+ const auto& hwcIntents =
+ iter != hwcColorModes.end() ? iter->second : std::vector<RenderIntent>();
+ const RenderIntent hwcIntent = getHwcRenderIntent(hwcIntents, intent);
+
+ const Dataspace dataspace = colorModeToDataspace(mode);
+ const Dataspace hwcDataspace = colorModeToDataspace(hwcColorMode);
+
+ ALOGV("DisplayDevice %d/%d: map (%s, %s) to (%s, %s, %s)", mType, mHwcDisplayId,
+ dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+ decodeRenderIntent(intent).c_str(),
+ dataspaceDetails(static_cast<android_dataspace_t>(hwcDataspace)).c_str(),
+ decodeColorMode(hwcColorMode).c_str(), decodeRenderIntent(hwcIntent).c_str());
+
+ mColorModes[getColorModeKey(dataspace, intent)] = {hwcDataspace, hwcColorMode, hwcIntent};
+}
+
+void DisplayDevice::populateColorModes(
+ const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes) {
+ if (!hasWideColorGamut()) {
+ return;
+ }
+
+ // collect all known SDR render intents
+ std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
+ sSdrRenderIntents.end());
+ auto iter = hwcColorModes.find(ColorMode::SRGB);
+ if (iter != hwcColorModes.end()) {
+ for (auto intent : iter->second) {
+ sdrRenderIntents.insert(intent);
}
}
+
+ // add all known SDR combinations
+ for (auto intent : sdrRenderIntents) {
+ for (auto mode : sSdrColorModes) {
+ addColorMode(hwcColorModes, mode, intent);
+ }
+ }
+
+ // collect all known HDR render intents
+ std::unordered_set<RenderIntent> hdrRenderIntents(sHdrRenderIntents.begin(),
+ sHdrRenderIntents.end());
+ iter = hwcColorModes.find(ColorMode::BT2100_PQ);
+ if (iter != hwcColorModes.end()) {
+ for (auto intent : iter->second) {
+ hdrRenderIntents.insert(intent);
+ }
+ }
+
+ // add all known HDR combinations
+ for (auto intent : sHdrRenderIntents) {
+ for (auto mode : sHdrColorModes) {
+ addColorMode(hwcColorModes, mode, intent);
+ }
+ }
+}
+
+bool DisplayDevice::hasRenderIntent(RenderIntent intent) const {
+ // assume a render intent is supported when SRGB supports it; we should
+ // get rid of that assumption.
+ auto iter = mColorModes.find(getColorModeKey(Dataspace::SRGB, intent));
+ return iter != mColorModes.end() && iter->second.renderIntent == intent;
+}
+
+bool DisplayDevice::hasModernHdrSupport(Dataspace dataspace) const {
+ if ((dataspace == Dataspace::BT2020_PQ && hasHDR10Support()) ||
+ (dataspace == Dataspace::BT2020_HLG && hasHLGSupport())) {
+ auto iter =
+ mColorModes.find(getColorModeKey(dataspace, RenderIntent::TONE_MAP_COLORIMETRIC));
+ return iter != mColorModes.end() && iter->second.dataspace == dataspace;
+ }
+
+ return false;
+}
+
+void DisplayDevice::getBestColorMode(Dataspace dataspace, RenderIntent intent,
+ Dataspace* outDataspace, ColorMode* outMode,
+ RenderIntent* outIntent) const {
+ auto iter = mColorModes.find(getColorModeKey(dataspace, intent));
+ if (iter != mColorModes.end()) {
+ *outDataspace = iter->second.dataspace;
+ *outMode = iter->second.colorMode;
+ *outIntent = iter->second.renderIntent;
+ } else {
+ ALOGE("map unknown (%s)/(%s) to default color mode",
+ dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+ decodeRenderIntent(intent).c_str());
+
+ *outDataspace = Dataspace::UNKNOWN;
+ *outMode = ColorMode::NATIVE;
+ *outIntent = RenderIntent::COLORIMETRIC;
+ }
}
std::atomic<int32_t> DisplayDeviceState::nextDisplayId(1);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index d051e33..b3859b6 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -91,7 +91,7 @@
bool hasWideColorGamut,
const HdrCapabilities& hdrCapabilities,
const int32_t supportedPerFrameMetadata,
- const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hdrAndRenderIntents,
+ const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes,
int initialPowerMode);
// clang-format on
@@ -143,11 +143,17 @@
// machine happy without actually queueing a buffer if nothing has changed
status_t beginFrame(bool mustRecompose) const;
status_t prepareFrame(HWComposer& hwc);
+
bool hasWideColorGamut() const { return mHasWideColorGamut; }
// Whether h/w composer has native support for specific HDR type.
bool hasHDR10Support() const { return mHasHdr10; }
bool hasHLGSupport() const { return mHasHLG; }
bool hasDolbyVisionSupport() const { return mHasDolbyVision; }
+
+ // Return true if the corresponding color mode for the HDR dataspace is
+ // supported.
+ bool hasModernHdrSupport(ui::Dataspace dataspace) const;
+
// The returned HdrCapabilities is the combination of HDR capabilities from
// hardware composer and RenderEngine. When the DisplayDevice supports wide
// color gamut, RenderEngine is able to simulate HDR support in Display P3
@@ -156,13 +162,12 @@
// respectively if hardware composer doesn't return meaningful values.
const HdrCapabilities& getHdrCapabilities() const { return mHdrCapabilities; }
- // Whether h/w composer has BT2100_PQ color mode.
- bool hasBT2100PQColorimetricSupport() const { return mHasBT2100PQColorimetric; }
- bool hasBT2100PQEnhanceSupport() const { return mHasBT2100PQEnhance; }
+ // Return true if intent is supported by the display.
+ bool hasRenderIntent(ui::RenderIntent intent) const;
- // Whether h/w composer has BT2100_HLG color mode.
- bool hasBT2100HLGColorimetricSupport() const { return mHasBT2100HLGColorimetric; }
- bool hasBT2100HLGEnhanceSupport() const { return mHasBT2100HLGEnhance; }
+ void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
+ ui::Dataspace* outDataspace, ui::ColorMode* outMode,
+ ui::RenderIntent* outIntent) const;
void swapBuffers(HWComposer& hwc) const;
@@ -214,9 +219,6 @@
void dump(String8& result) const;
private:
- void hasToneMapping(const std::vector<ui::RenderIntent>& renderIntents,
- bool* outColorimetric, bool *outEnhance);
-
/*
* Constants, set during initialization
*/
@@ -272,10 +274,10 @@
// Current active config
int mActiveConfig;
// current active color mode
- ui::ColorMode mActiveColorMode;
+ ui::ColorMode mActiveColorMode = ui::ColorMode::NATIVE;
// Current active render intent.
- ui::RenderIntent mActiveRenderIntent;
- ui::Dataspace mCompositionDataSpace;
+ ui::RenderIntent mActiveRenderIntent = ui::RenderIntent::COLORIMETRIC;
+ ui::Dataspace mCompositionDataSpace = ui::Dataspace::UNKNOWN;
// Current color transform
android_color_transform_t mColorTransform;
@@ -288,12 +290,26 @@
bool mHasDolbyVision;
HdrCapabilities mHdrCapabilities;
const int32_t mSupportedPerFrameMetadata;
- // Whether h/w composer has BT2100_PQ and BT2100_HLG color mode with
- // colorimetrical tone mapping or enhanced tone mapping.
- bool mHasBT2100PQColorimetric;
- bool mHasBT2100PQEnhance;
- bool mHasBT2100HLGColorimetric;
- bool mHasBT2100HLGEnhance;
+
+ // Mappings from desired Dataspace/RenderIntent to the supported
+ // Dataspace/ColorMode/RenderIntent.
+ using ColorModeKey = uint64_t;
+ struct ColorModeValue {
+ ui::Dataspace dataspace;
+ ui::ColorMode colorMode;
+ ui::RenderIntent renderIntent;
+ };
+
+ static ColorModeKey getColorModeKey(ui::Dataspace dataspace, ui::RenderIntent intent) {
+ return (static_cast<uint64_t>(dataspace) << 32) | static_cast<uint32_t>(intent);
+ }
+ void populateColorModes(
+ const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes);
+ void addColorMode(
+ const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes,
+ const ui::ColorMode mode, const ui::RenderIntent intent);
+
+ std::unordered_map<ColorModeKey, ColorModeValue> mColorModes;
};
struct DisplayDeviceState {
@@ -336,13 +352,6 @@
bool isSecure() const override { return mDevice->isSecure(); }
bool needsFiltering() const override { return mDevice->needsFiltering(); }
Rect getSourceCrop() const override { return mSourceCrop; }
- bool getWideColorSupport() const override { return mDevice->hasWideColorGamut(); }
- ui::Dataspace getDataSpace() const override {
- return mDevice->getCompositionDataSpace();
- }
- float getDisplayMaxLuminance() const override {
- return mDevice->getHdrCapabilities().getDesiredMaxLuminance();
- }
private:
const sp<const DisplayDevice> mDevice;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 3947318..61758b6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -30,8 +30,9 @@
#include <android/configuration.h>
-#include <algorithm>
#include <inttypes.h>
+#include <algorithm>
+#include <iterator>
#include <set>
using android::Fence;
@@ -338,6 +339,31 @@
return Error::None;
}
+Error Display::getActiveConfigIndex(int* outIndex) const {
+ ALOGV("[%" PRIu64 "] getActiveConfigIndex", mId);
+ hwc2_config_t configId = 0;
+ auto intError = mComposer.getActiveConfig(mId, &configId);
+ auto error = static_cast<Error>(intError);
+
+ if (error != Error::None) {
+ ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId);
+ *outIndex = -1;
+ return error;
+ }
+
+ auto pos = mConfigs.find(configId);
+ if (pos != mConfigs.end()) {
+ *outIndex = std::distance(mConfigs.begin(), pos);
+ } else {
+ ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId, configId);
+ // Return no error, but the caller needs to check for a negative index
+ // to detect this case
+ *outIndex = -1;
+ }
+
+ return Error::None;
+}
+
Error Display::getChangedCompositionTypes(
std::unordered_map<Layer*, Composition>* outTypes)
{
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 3ac06ec..29d7a47 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -36,7 +36,6 @@
#include <unordered_map>
#include <unordered_set>
#include <vector>
-#include <map>
namespace android {
class Fence;
@@ -207,6 +206,7 @@
[[clang::warn_unused_result]] Error destroyLayer(Layer* layer);
[[clang::warn_unused_result]] Error getActiveConfig(
std::shared_ptr<const Config>* outConfig) const;
+ [[clang::warn_unused_result]] Error getActiveConfigIndex(int* outIndex) const;
[[clang::warn_unused_result]] Error getChangedCompositionTypes(
std::unordered_map<Layer*, Composition>* outTypes);
[[clang::warn_unused_result]] Error getColorModes(
@@ -288,9 +288,7 @@
bool mIsConnected;
DisplayType mType;
std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
- // The ordering in this map matters, for getConfigs(), when it is
- // converted to a vector
- std::map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+ std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
};
// Convenience C++ class to access hwc2_device_t Layer functions directly.
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 96d691c..f5f7a82 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -318,6 +318,28 @@
return config;
}
+int HWComposer::getActiveConfigIndex(int32_t displayId) const {
+ if (!isValidDisplay(displayId)) {
+ ALOGV("getActiveConfigIndex: Attempted to access invalid display %d", displayId);
+ return -1;
+ }
+ int index;
+ auto error = mDisplayData[displayId].hwcDisplay->getActiveConfigIndex(&index);
+ if (error == HWC2::Error::BadConfig) {
+ ALOGE("getActiveConfigIndex: No config active, returning -1");
+ return -1;
+ } else if (error != HWC2::Error::None) {
+ ALOGE("getActiveConfigIndex failed for display %d: %s (%d)", displayId,
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ return -1;
+ } else if (index < 0) {
+ ALOGE("getActiveConfigIndex returned an unknown config for display %d", displayId);
+ return -1;
+ }
+
+ return index;
+}
+
std::vector<ui::ColorMode> HWComposer::getColorModes(int32_t displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, {});
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index d7f3b08..f968948 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -164,6 +164,7 @@
std::shared_ptr<const HWC2::Display::Config>
getActiveConfig(int32_t displayId) const;
+ int getActiveConfigIndex(int32_t displayId) const;
std::vector<ui::ColorMode> getColorModes(int32_t displayId) const;
diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/EventControlThread.h
index 9be4e7c..cafae53 100644
--- a/services/surfaceflinger/EventControlThread.h
+++ b/services/surfaceflinger/EventControlThread.h
@@ -26,8 +26,6 @@
namespace android {
-class SurfaceFlinger;
-
class EventControlThread {
public:
virtual ~EventControlThread();
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index bb9c070..bc271c8 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -26,14 +26,12 @@
#include <cutils/sched_policy.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/Trace.h>
#include "EventThread.h"
-#include "SurfaceFlinger.h"
using namespace std::chrono_literals;
@@ -47,9 +45,11 @@
namespace impl {
-EventThread::EventThread(VSyncSource* src, SurfaceFlinger& flinger, bool interceptVSyncs,
- const char* threadName)
- : mVSyncSource(src), mFlinger(flinger), mInterceptVSyncs(interceptVSyncs) {
+EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
+ InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
+ : mVSyncSource(src),
+ mResyncWithRateLimitCallback(resyncWithRateLimitCallback),
+ mInterceptVSyncsCallback(interceptVSyncsCallback) {
for (auto& event : mVSyncEvent) {
event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
event.header.id = 0;
@@ -118,7 +118,9 @@
void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) {
std::lock_guard<std::mutex> lock(mMutex);
- mFlinger.resyncWithRateLimit();
+ if (mResyncWithRateLimitCallback) {
+ mResyncWithRateLimitCallback();
+ }
if (connection->count < 0) {
connection->count = 0;
@@ -216,8 +218,8 @@
timestamp = mVSyncEvent[i].header.timestamp;
if (timestamp) {
// we have a vsync event to dispatch
- if (mInterceptVSyncs) {
- mFlinger.mInterceptor->saveVSyncEvent(timestamp);
+ if (mInterceptVSyncsCallback) {
+ mInterceptVSyncsCallback(timestamp);
}
*event = mVSyncEvent[i];
mVSyncEvent[i].header.timestamp = 0;
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 97f0a35..9c13ed2 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -37,6 +37,7 @@
namespace android {
// ---------------------------------------------------------------------------
+class EventThreadTest;
class SurfaceFlinger;
class String8;
@@ -82,7 +83,9 @@
class Connection : public BnDisplayEventConnection {
public:
explicit Connection(EventThread* eventThread);
- status_t postEvent(const DisplayEventReceiver::Event& event);
+ virtual ~Connection();
+
+ virtual status_t postEvent(const DisplayEventReceiver::Event& event);
// count >= 1 : continuous event. count is the vsync rate
// count == 0 : one-shot event that has not fired
@@ -90,7 +93,6 @@
int32_t count;
private:
- virtual ~Connection();
virtual void onFirstRef();
status_t stealReceiveChannel(gui::BitTube* outChannel) override;
status_t setVsyncRate(uint32_t count) override;
@@ -100,8 +102,11 @@
};
public:
- EventThread(VSyncSource* src, SurfaceFlinger& flinger, bool interceptVSyncs,
- const char* threadName);
+ using ResyncWithRateLimitCallback = std::function<void()>;
+ using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
+
+ EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
+ InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
~EventThread();
sp<BnDisplayEventConnection> createEventConnection() const override;
@@ -124,6 +129,8 @@
void setPhaseOffset(nsecs_t phaseOffset) override;
private:
+ friend EventThreadTest;
+
void threadMain();
Vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock,
DisplayEventReceiver::Event* event)
@@ -137,8 +144,9 @@
void onVSyncEvent(nsecs_t timestamp) override;
// constants
- VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
- SurfaceFlinger& mFlinger;
+ VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr;
+ const ResyncWithRateLimitCallback mResyncWithRateLimitCallback;
+ const InterceptVSyncsCallback mInterceptVSyncsCallback;
std::thread mThread;
mutable std::mutex mMutex;
@@ -155,8 +163,6 @@
// for debugging
bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false;
-
- const bool mInterceptVSyncs = false;
};
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9043234..2595ec1 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -98,8 +98,9 @@
mQueueItems(),
mLastFrameNumberReceived(0),
mAutoRefresh(false),
- mFreezeGeometryUpdates(false) {
-
+ mFreezeGeometryUpdates(false),
+ mCurrentChildren(LayerVector::StateSet::Current),
+ mDrawingChildren(LayerVector::StateSet::Drawing) {
mCurrentCrop.makeInvalid();
uint32_t layerFlags = 0;
@@ -123,7 +124,6 @@
mCurrentState.layerStack = 0;
mCurrentState.sequence = 0;
mCurrentState.requested = mCurrentState.active;
- mCurrentState.dataSpace = ui::Dataspace::UNKNOWN;
mCurrentState.appId = 0;
mCurrentState.type = 0;
@@ -138,7 +138,6 @@
CompositorTiming compositorTiming;
flinger->getCompositorTiming(&compositorTiming);
mFrameEventHistory.initializeCompositorTiming(compositorTiming);
-
}
void Layer::onFirstRef() {}
@@ -921,10 +920,7 @@
}
void Layer::popPendingState(State* stateToCommit) {
- auto oldFlags = stateToCommit->flags;
*stateToCommit = mPendingStates[0];
- stateToCommit->flags =
- (oldFlags & ~stateToCommit->mask) | (stateToCommit->flags & stateToCommit->mask);
mPendingStates.removeAt(0);
ATRACE_INT(mTransactionName.string(), mPendingStates.size());
@@ -1271,7 +1267,6 @@
if (mCurrentState.flags == newFlags) return false;
mCurrentState.sequence++;
mCurrentState.flags = newFlags;
- mCurrentState.mask = mask;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1328,19 +1323,6 @@
return true;
}
-bool Layer::setDataSpace(ui::Dataspace dataSpace) {
- if (mCurrentState.dataSpace == dataSpace) return false;
- mCurrentState.sequence++;
- mCurrentState.dataSpace = dataSpace;
- mCurrentState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-ui::Dataspace Layer::getDataSpace() const {
- return mCurrentState.dataSpace;
-}
-
uint32_t Layer::getLayerStack() const {
auto p = mDrawingParent.promote();
if (p == nullptr) {
@@ -1433,7 +1415,7 @@
info.mColor = ds.color;
info.mFlags = ds.flags;
info.mPixelFormat = getPixelFormat();
- info.mDataSpace = static_cast<android_dataspace>(getDataSpace());
+ info.mDataSpace = static_cast<android_dataspace>(mCurrentDataSpace);
info.mMatrix[0][0] = ds.active.transform[0][0];
info.mMatrix[0][1] = ds.active.transform[0][1];
info.mMatrix[1][0] = ds.active.transform[1][0];
@@ -1652,7 +1634,7 @@
bool Layer::isLegacyDataSpace() const {
// return true when no higher bits are set
- return !(mDrawingState.dataSpace & (ui::Dataspace::STANDARD_MASK |
+ return !(mCurrentDataSpace & (ui::Dataspace::STANDARD_MASK |
ui::Dataspace::TRANSFER_MASK | ui::Dataspace::RANGE_MASK));
}
@@ -1695,7 +1677,7 @@
return children;
}
- LayerVector traverse;
+ LayerVector traverse(stateSet);
for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
sp<Layer> strongRelative = weakRelative.promote();
if (strongRelative != nullptr) {
@@ -1793,7 +1775,7 @@
const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
const State& state = useDrawing ? mDrawingState : mCurrentState;
- LayerVector traverse;
+ LayerVector traverse(stateSet);
for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
sp<Layer> strongRelative = weakRelative.promote();
// Only add relative layers that are also descendents of the top most parent of the tree.
@@ -1960,7 +1942,10 @@
layerInfo->set_is_opaque(isOpaque(state));
layerInfo->set_invalidate(contentDirty);
- layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
+
+ // XXX (b/79210409) mCurrentDataSpace is not protected
+ layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(mCurrentDataSpace)));
+
layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
LayerProtoHelper::writeToProto(getColor(), layerInfo->mutable_color());
LayerProtoHelper::writeToProto(state.color, layerInfo->mutable_requested_color());
@@ -1979,6 +1964,7 @@
layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
}
+ // XXX getBE().compositionInfo.mBuffer is not protected
auto buffer = getBE().compositionInfo.mBuffer;
if (buffer != nullptr) {
LayerProtoHelper::writeToProto(buffer, layerInfo->mutable_active_buffer());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 632efbe..ae80043 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -186,7 +186,6 @@
uint32_t layerStack;
uint8_t flags;
- uint8_t mask;
uint8_t reserved[2];
int32_t sequence; // changes when visible regions can change
bool modified;
@@ -209,7 +208,6 @@
// dependent.
Region activeTransparentRegion;
Region requestedTransparentRegion;
- ui::Dataspace dataSpace;
int32_t appId;
int32_t type;
@@ -287,8 +285,6 @@
bool setTransparentRegionHint(const Region& transparent);
bool setFlags(uint8_t flags, uint8_t mask);
bool setLayerStack(uint32_t layerStack);
- bool setDataSpace(ui::Dataspace dataSpace);
- ui::Dataspace getDataSpace() const;
uint32_t getLayerStack() const;
void deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber);
void deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber);
@@ -299,6 +295,8 @@
bool reparent(const sp<IBinder>& newParentHandle);
bool detachChildren();
+ ui::Dataspace getDataSpace() const { return mCurrentDataSpace; }
+
// Before color management is introduced, contents on Android have to be
// desaturated in order to match what they appears like visually.
// With color management, these contents will appear desaturated, thus
@@ -589,6 +587,7 @@
// SurfaceFlinger to complete a transaction.
void commitChildList();
int32_t getZ() const;
+ void pushPendingState();
protected:
// constant
@@ -671,7 +670,6 @@
// Returns false if the relevant frame has already been latched
bool addSyncPoint(const std::shared_ptr<SyncPoint>& point);
- void pushPendingState();
void popPendingState(State* stateToCommit);
bool applyPendingStates(State* stateToCommit);
@@ -744,6 +742,7 @@
int mActiveBufferSlot;
sp<GraphicBuffer> mActiveBuffer;
sp<NativeHandle> mSidebandStream;
+ ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN;
Rect mCurrentCrop;
uint32_t mCurrentTransform;
// We encode unset as -1.
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index 47156c1..8494524 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -19,26 +19,37 @@
namespace android {
-LayerVector::LayerVector() = default;
+LayerVector::LayerVector(const StateSet stateSet) : mStateSet(stateSet) {}
-LayerVector::LayerVector(const LayerVector& rhs) : SortedVector<sp<Layer>>(rhs) {
-}
+LayerVector::LayerVector(const LayerVector& rhs, const StateSet stateSet)
+ : SortedVector<sp<Layer>>(rhs), mStateSet(stateSet) {}
LayerVector::~LayerVector() = default;
+// This operator override is needed to prevent mStateSet from getting copied over.
+LayerVector& LayerVector::operator=(const LayerVector& rhs) {
+ SortedVector::operator=(rhs);
+ return *this;
+}
+
int LayerVector::do_compare(const void* lhs, const void* rhs) const
{
// sort layers per layer-stack, then by z-order and finally by sequence
const auto& l = *reinterpret_cast<const sp<Layer>*>(lhs);
const auto& r = *reinterpret_cast<const sp<Layer>*>(rhs);
- uint32_t ls = l->getCurrentState().layerStack;
- uint32_t rs = r->getCurrentState().layerStack;
+ const auto& lState =
+ (mStateSet == StateSet::Current) ? l->getCurrentState() : l->getDrawingState();
+ const auto& rState =
+ (mStateSet == StateSet::Current) ? r->getCurrentState() : r->getDrawingState();
+
+ uint32_t ls = lState.layerStack;
+ uint32_t rs = rState.layerStack;
if (ls != rs)
return (ls > rs) ? 1 : -1;
- int32_t lz = l->getCurrentState().z;
- int32_t rz = r->getCurrentState().z;
+ int32_t lz = lState.z;
+ int32_t rz = rState.z;
if (lz != rz)
return (lz > rz) ? 1 : -1;
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
index a9adb41..88d7711 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -32,22 +32,27 @@
*/
class LayerVector : public SortedVector<sp<Layer>> {
public:
- LayerVector();
- LayerVector(const LayerVector& rhs);
- ~LayerVector() override;
-
enum class StateSet {
Invalid,
Current,
Drawing,
};
+ explicit LayerVector(const StateSet stateSet);
+ LayerVector(const LayerVector& rhs, const StateSet stateSet);
+ ~LayerVector() override;
+
+ LayerVector& operator=(const LayerVector& rhs);
+
// Sorts layer by layer-stack, Z order, and finally creation order (sequence).
int do_compare(const void* lhs, const void* rhs) const override;
using Visitor = std::function<void(Layer*)>;
void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const;
void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const;
+
+private:
+ const StateSet mStateSet;
};
}
diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp
index 46ec8e6..1a8edf3 100644
--- a/services/surfaceflinger/RenderArea.cpp
+++ b/services/surfaceflinger/RenderArea.cpp
@@ -1,5 +1,7 @@
#include "RenderArea.h"
+#include <gui/LayerState.h>
+
namespace android {
float RenderArea::getCaptureFillValue(CaptureFill captureFill) {
@@ -15,7 +17,7 @@
* Checks that the requested width and height are valid and updates them to the render area
* dimensions if they are set to 0
*/
-status_t RenderArea::updateDimensions() {
+status_t RenderArea::updateDimensions(int displayRotation) {
// get screen geometry
uint32_t width = getWidth();
@@ -25,6 +27,10 @@
std::swap(width, height);
}
+ if (displayRotation & DisplayState::eOrientationSwapMask) {
+ std::swap(width, height);
+ }
+
if ((mReqWidth > width) || (mReqHeight > height)) {
ALOGE("size mismatch (%d, %d) > (%d, %d)", mReqWidth, mReqHeight, width, height);
return BAD_VALUE;
@@ -40,4 +46,4 @@
return NO_ERROR;
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 4694403..96e4b5f 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -30,16 +30,13 @@
virtual bool isSecure() const = 0;
virtual bool needsFiltering() const = 0;
virtual Rect getSourceCrop() const = 0;
- virtual bool getWideColorSupport() const = 0;
- virtual ui::Dataspace getDataSpace() const = 0;
- virtual float getDisplayMaxLuminance() const = 0;
virtual void render(std::function<void()> drawLayers) { drawLayers(); }
int getReqHeight() const { return mReqHeight; };
int getReqWidth() const { return mReqWidth; };
Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
- status_t updateDimensions();
+ status_t updateDimensions(int displayRotation);
CaptureFill getCaptureFill() const { return mCaptureFill; };
diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp
index c218e4d..09414fd 100644
--- a/services/surfaceflinger/RenderEngine/Description.cpp
+++ b/services/surfaceflinger/RenderEngine/Description.cpp
@@ -51,6 +51,10 @@
mProjectionMatrix = mtx;
}
+void Description::setSaturationMatrix(const mat4& mtx) {
+ mSaturationMatrix = mtx;
+}
+
void Description::setColorMatrix(const mat4& mtx) {
mColorMatrix = mtx;
}
@@ -78,6 +82,11 @@
return mColorMatrix != identity;
}
+bool Description::hasSaturationMatrix() const {
+ const mat4 identity;
+ return mSaturationMatrix != identity;
+}
+
const mat4& Description::getColorMatrix() const {
return mColorMatrix;
}
diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h
index 6ebb340..06eaf35 100644
--- a/services/surfaceflinger/RenderEngine/Description.h
+++ b/services/surfaceflinger/RenderEngine/Description.h
@@ -42,12 +42,14 @@
void disableTexture();
void setColor(const half4& color);
void setProjectionMatrix(const mat4& mtx);
+ void setSaturationMatrix(const mat4& mtx);
void setColorMatrix(const mat4& mtx);
void setInputTransformMatrix(const mat3& matrix);
void setOutputTransformMatrix(const mat4& matrix);
bool hasInputTransformMatrix() const;
bool hasOutputTransformMatrix() const;
bool hasColorMatrix() const;
+ bool hasSaturationMatrix() const;
const mat4& getColorMatrix() const;
void setY410BT2020(bool enable);
@@ -90,6 +92,7 @@
// projection matrix
mat4 mProjectionMatrix;
mat4 mColorMatrix;
+ mat4 mSaturationMatrix;
mat3 mInputTransformMatrix;
mat4 mOutputTransformMatrix;
};
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index 64095dd..90404fa 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -147,6 +147,7 @@
mSrgbToXyz = srgb.getRGBtoXYZ();
mDisplayP3ToXyz = displayP3.getRGBtoXYZ();
mBt2020ToXyz = bt2020.getRGBtoXYZ();
+ mXyzToSrgb = mat4(srgb.getXYZtoRGB());
mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB());
mXyzToBt2020 = mat4(bt2020.getXYZtoRGB());
}
@@ -263,6 +264,10 @@
mState.setColorMatrix(colorTransform);
}
+void GLES20RenderEngine::setSaturationMatrix(const mat4& saturationMatrix) {
+ mState.setSaturationMatrix(saturationMatrix);
+}
+
void GLES20RenderEngine::disableTexturing() {
mState.disableTexture();
}
@@ -343,14 +348,17 @@
break;
}
- // The supported output color spaces are Display P3 and BT2020.
+ // The supported output color spaces are BT2020, Display P3 and standard RGB.
switch (outputStandard) {
case Dataspace::STANDARD_BT2020:
wideColorState.setOutputTransformMatrix(mXyzToBt2020);
break;
- default:
+ case Dataspace::STANDARD_DCI_P3:
wideColorState.setOutputTransformMatrix(mXyzToDisplayP3);
break;
+ default:
+ wideColorState.setOutputTransformMatrix(mXyzToSrgb);
+ break;
}
} else if (inputStandard != outputStandard) {
// At this point, the input data space and output data space could be both
@@ -372,10 +380,11 @@
// we need to convert the RGB value to linear space and convert it back when:
// - there is a color matrix that is not an identity matrix, or
+ // - there is a saturation matrix that is not an identity matrix, or
// - there is an output transform matrix that is not an identity matrix, or
// - the input transfer function doesn't match the output transfer function.
- if (wideColorState.hasColorMatrix() || wideColorState.hasOutputTransformMatrix() ||
- inputTransfer != outputTransfer) {
+ if (wideColorState.hasColorMatrix() || wideColorState.hasSaturationMatrix() ||
+ wideColorState.hasOutputTransformMatrix() || inputTransfer != outputTransfer) {
switch (inputTransfer) {
case Dataspace::TRANSFER_ST2084:
wideColorState.setInputTransferFunction(Description::TransferFunction::ST2084);
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
index c9e402d..de5761b 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
@@ -81,6 +81,7 @@
virtual void setupLayerBlackedOut();
virtual void setupFillWithColor(float r, float g, float b, float a);
virtual void setupColorTransform(const mat4& colorTransform);
+ virtual void setSaturationMatrix(const mat4& saturationMatrix);
virtual void disableTexturing();
virtual void disableBlending();
@@ -102,6 +103,7 @@
mat3 mSrgbToXyz;
mat3 mBt2020ToXyz;
mat3 mDisplayP3ToXyz;
+ mat4 mXyzToSrgb;
mat4 mXyzToDisplayP3;
mat4 mXyzToBt2020;
diff --git a/services/surfaceflinger/RenderEngine/Program.cpp b/services/surfaceflinger/RenderEngine/Program.cpp
index fd2c968..95adaca 100644
--- a/services/surfaceflinger/RenderEngine/Program.cpp
+++ b/services/surfaceflinger/RenderEngine/Program.cpp
@@ -135,13 +135,22 @@
glUniform4fv(mColorLoc, 1, color);
}
if (mInputTransformMatrixLoc >= 0) {
- glUniformMatrix3fv(mInputTransformMatrixLoc, 1, GL_FALSE,
- desc.mInputTransformMatrix.asArray());
+ // If the input transform matrix is not identity matrix, we want to merge
+ // the saturation matrix with input transform matrix so that the saturation
+ // matrix is applied at the correct stage.
+ mat4 inputTransformMatrix = mat4(desc.mInputTransformMatrix) * desc.mSaturationMatrix;
+ glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
}
if (mOutputTransformMatrixLoc >= 0) {
// The output transform matrix and color matrix can be combined as one matrix
// that is applied right before applying OETF.
mat4 outputTransformMatrix = desc.mColorMatrix * desc.mOutputTransformMatrix;
+ // If there is no input transform matrix, we want to merge the saturation
+ // matrix with output transform matrix to avoid extra matrix multiplication
+ // in shader.
+ if (mInputTransformMatrixLoc < 0) {
+ outputTransformMatrix *= desc.mSaturationMatrix;
+ }
glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE,
outputTransformMatrix.asArray());
}
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index abb0290..5b61db9 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -129,7 +129,8 @@
description.hasInputTransformMatrix() ?
Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF)
.set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
- description.hasOutputTransformMatrix() || description.hasColorMatrix() ?
+ description.hasOutputTransformMatrix() || description.hasColorMatrix() ||
+ (!description.hasInputTransformMatrix() && description.hasSaturationMatrix()) ?
Key::OUTPUT_TRANSFORM_MATRIX_ON : Key::OUTPUT_TRANSFORM_MATRIX_OFF);
needs.set(Key::Y410_BT2020_MASK,
@@ -309,20 +310,20 @@
float y2 = y1 + (maxOutLumi - y1) * 0.75;
// horizontal distances between the last three control points
- const float h12 = x2 - x1;
- const float h23 = maxInLumi - x2;
+ float h12 = x2 - x1;
+ float h23 = maxInLumi - x2;
// tangents at the last three control points
- const float m1 = (y2 - y1) / h12;
- const float m3 = (maxOutLumi - y2) / h23;
- const float m2 = (m1 + m3) / 2.0;
+ float m1 = (y2 - y1) / h12;
+ float m3 = (maxOutLumi - y2) / h23;
+ float m2 = (m1 + m3) / 2.0;
if (nits < x0) {
// scale [0.0, x0] to [0.0, y0] linearly
- const float slope = y0 / x0;
+ float slope = y0 / x0;
nits *= slope;
} else if (nits < x1) {
// scale [x0, x1] to [y0, y1] linearly
- const float slope = (y1 - y0) / (x1 - x0);
+ float slope = (y1 - y0) / (x1 - x0);
nits = y0 + (nits - x0) * slope;
} else if (nits < x2) {
// scale [x1, x2] to [y1, y2] using Hermite interp
@@ -344,11 +345,46 @@
}
break;
default:
- // TODO(73825729) We need to revert the tone mapping in
- // hardware composer properly.
+ // inverse tone map; the output luminance can be up to maxOutLumi.
fs << R"__SHADER__(
highp vec3 ToneMap(highp vec3 color) {
- return color;
+ const float maxOutLumi = 3000.0;
+
+ const float x0 = 5.0;
+ const float y0 = 2.5;
+ float x1 = displayMaxLuminance * 0.7;
+ float y1 = maxOutLumi * 0.15;
+ float x2 = displayMaxLuminance * 0.9;
+ float y2 = maxOutLumi * 0.45;
+ float x3 = displayMaxLuminance;
+ float y3 = maxOutLumi;
+
+ float c1 = y1 / 3.0;
+ float c2 = y2 / 2.0;
+ float c3 = y3 / 1.5;
+
+ float nits = color.y;
+
+ float scale;
+ if (nits <= x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ const float slope = y0 / x0;
+ nits *= slope;
+ } else if (nits <= x1) {
+ // scale [x0, x1] to [y0, y1] using a curve
+ float t = (nits - x0) / (x1 - x0);
+ nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1;
+ } else if (nits <= x2) {
+ // scale [x1, x2] to [y1, y2] using a curve
+ float t = (nits - x1) / (x2 - x1);
+ nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2;
+ } else {
+ // scale [x2, x3] to [y2, y3] using a curve
+ float t = (nits - x2) / (x3 - x2);
+ nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3;
+ }
+
+ return color * (nits / max(1e-6, color.y));
}
)__SHADER__";
break;
@@ -421,13 +457,13 @@
case Key::OUTPUT_TF_ST2084:
fs << R"__SHADER__(
vec3 OETF(const vec3 linear) {
- const float m1 = (2610.0 / 4096.0) / 4.0;
- const float m2 = (2523.0 / 4096.0) * 128.0;
- const float c1 = (3424.0 / 4096.0);
- const float c2 = (2413.0 / 4096.0) * 32.0;
- const float c3 = (2392.0 / 4096.0) * 32.0;
+ const highp float m1 = (2610.0 / 4096.0) / 4.0;
+ const highp float m2 = (2523.0 / 4096.0) * 128.0;
+ const highp float c1 = (3424.0 / 4096.0);
+ const highp float c2 = (2413.0 / 4096.0) * 32.0;
+ const highp float c3 = (2392.0 / 4096.0) * 32.0;
- vec3 tmp = pow(linear, vec3(m1));
+ highp vec3 tmp = pow(linear, vec3(m1));
tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
return pow(tmp, vec3(m2));
}
@@ -518,10 +554,10 @@
}
if (needs.hasInputTransformMatrix()) {
- fs << "uniform mat3 inputTransformMatrix;";
+ fs << "uniform mat4 inputTransformMatrix;";
fs << R"__SHADER__(
highp vec3 InputTransform(const highp vec3 color) {
- return inputTransformMatrix * color;
+ return vec3(inputTransformMatrix * vec4(color, 1.0));
}
)__SHADER__";
} else {
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index d559464..a14acaa 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -113,6 +113,7 @@
virtual void setupFillWithColor(float r, float g, float b, float a) = 0;
virtual void setupColorTransform(const mat4& /* colorTransform */) = 0;
+ virtual void setSaturationMatrix(const mat4& /* saturationMatrix */) = 0;
virtual void disableTexturing() = 0;
virtual void disableBlending() = 0;
@@ -225,6 +226,7 @@
void checkErrors() const override;
void setupColorTransform(const mat4& /* colorTransform */) override {}
+ void setSaturationMatrix(const mat4& /* saturationMatrix */) override {}
// internal to RenderEngine
EGLDisplay getEGLDisplay() const;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 588d24c..97edd35 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -88,6 +88,8 @@
#include <cutils/compiler.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/types.h>
#include <configstore/Utils.h>
#include <layerproto/LayerProtoParser.h>
@@ -158,29 +160,18 @@
return std::string(value) == "true";
}
-DisplayColorSetting toDisplayColorSetting(int value) {
- switch(value) {
- case 0:
- return DisplayColorSetting::MANAGED;
- case 1:
- return DisplayColorSetting::UNMANAGED;
- case 2:
- return DisplayColorSetting::ENHANCED;
- default:
- return DisplayColorSetting::MANAGED;
- }
-}
-
std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
switch(displayColorSetting) {
case DisplayColorSetting::MANAGED:
- return std::string("Natural Mode");
+ return std::string("Managed");
case DisplayColorSetting::UNMANAGED:
- return std::string("Saturated Mode");
+ return std::string("Unmanaged");
case DisplayColorSetting::ENHANCED:
- return std::string("Auto Color Mode");
+ return std::string("Enhanced");
+ default:
+ return std::string("Unknown ") +
+ std::to_string(static_cast<int>(displayColorSetting));
}
- return std::string("Unknown Display Color Setting");
}
NativeWindowSurface::~NativeWindowSurface() = default;
@@ -282,6 +273,26 @@
hasWideColorDisplay =
getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+ V1_1::DisplayOrientation primaryDisplayOrientation =
+ getDisplayOrientation< V1_1::ISurfaceFlingerConfigs, &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>(
+ V1_1::DisplayOrientation::ORIENTATION_0);
+
+ switch (primaryDisplayOrientation) {
+ case V1_1::DisplayOrientation::ORIENTATION_90:
+ mPrimaryDisplayOrientation = DisplayState::eOrientation90;
+ break;
+ case V1_1::DisplayOrientation::ORIENTATION_180:
+ mPrimaryDisplayOrientation = DisplayState::eOrientation180;
+ break;
+ case V1_1::DisplayOrientation::ORIENTATION_270:
+ mPrimaryDisplayOrientation = DisplayState::eOrientation270;
+ break;
+ default:
+ mPrimaryDisplayOrientation = DisplayState::eOrientationDefault;
+ break;
+ }
+ ALOGV("Primary Display Orientation is set to %2d.", mPrimaryDisplayOrientation);
+
mPrimaryDispSync.init(SurfaceFlinger::hasSyncFramework, SurfaceFlinger::dispSyncPresentTimeOffset);
// debugging stuff...
@@ -316,8 +327,7 @@
mLayerTripleBufferingDisabled = atoi(value);
ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
- // TODO (b/74616334): Reduce the default value once we isolate the leak
- const size_t defaultListSize = 4 * MAX_LAYERS;
+ const size_t defaultListSize = MAX_LAYERS;
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
@@ -636,14 +646,21 @@
mEventThreadSource =
std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs,
true, "app");
- mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(), *this, false,
+ mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(),
+ [this]() { resyncWithRateLimit(); },
+ impl::EventThread::InterceptVSyncsCallback(),
"appEventThread");
mSfEventThreadSource =
std::make_unique<DispSyncSource>(&mPrimaryDispSync,
SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
- mSFEventThread = std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), *this, true,
- "sfEventThread");
+ mSFEventThread =
+ std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
+ [this]() { resyncWithRateLimit(); },
+ [this](nsecs_t timestamp) {
+ mInterceptor->saveVSyncEvent(timestamp);
+ },
+ "sfEventThread");
mEventQueue->setEventThread(mSFEventThread.get());
mVsyncModulator.setEventThread(mSFEventThread.get());
@@ -732,9 +749,7 @@
ALOGV("Saturation is set to %.2f", mGlobalSaturationFactor);
property_get("persist.sys.sf.native_mode", value, "0");
- mDisplayColorSetting = toDisplayColorSetting(atoi(value));
- ALOGV("Display Color Setting is set to %s.",
- decodeDisplayColorSetting(mDisplayColorSetting).c_str());
+ mDisplayColorSetting = static_cast<DisplayColorSetting>(atoi(value));
}
void SurfaceFlinger::startBootAnim() {
@@ -889,6 +904,11 @@
// All non-virtual displays are currently considered secure.
info.secure = true;
+ if (type == DisplayDevice::DISPLAY_PRIMARY &&
+ mPrimaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
+ std::swap(info.w, info.h);
+ }
+
configs->push_back(info);
}
@@ -1132,9 +1152,11 @@
ALOGV("VSync Injections enabled");
if (mVSyncInjector.get() == nullptr) {
mVSyncInjector = std::make_unique<InjectVSyncSource>();
- mInjectorEventThread =
- std::make_unique<impl::EventThread>(mVSyncInjector.get(), *this, false,
- "injEventThread");
+ mInjectorEventThread = std::make_unique<
+ impl::EventThread>(mVSyncInjector.get(),
+ [this]() { resyncWithRateLimit(); },
+ impl::EventThread::InterceptVSyncsCallback(),
+ "injEventThread");
}
mEventQueue->setEventThread(mInjectorEventThread.get());
} else {
@@ -1527,7 +1549,7 @@
mHadClientComposition = mHadClientComposition ||
getBE().mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
}
- mVsyncModulator.setLastFrameUsedRenderEngine(mHadClientComposition);
+ mVsyncModulator.onRefreshed(mHadClientComposition);
mLayersWithQueuedFrames.clear();
}
@@ -1860,7 +1882,6 @@
// can only be one of
// - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced)
// - Dataspace::DISPLAY_P3
-// - Dataspace::V0_SCRGB_LINEAR
// The returned HDR data space is one of
// - Dataspace::UNKNOWN
// - Dataspace::BT2020_HLG
@@ -1874,12 +1895,8 @@
switch (layer->getDataSpace()) {
case Dataspace::V0_SCRGB:
case Dataspace::V0_SCRGB_LINEAR:
- bestDataSpace = Dataspace::V0_SCRGB_LINEAR;
- break;
case Dataspace::DISPLAY_P3:
- if (bestDataSpace == Dataspace::SRGB) {
- bestDataSpace = Dataspace::DISPLAY_P3;
- }
+ bestDataSpace = Dataspace::DISPLAY_P3;
break;
case Dataspace::BT2020_PQ:
case Dataspace::BT2020_ITU_PQ:
@@ -1915,104 +1932,28 @@
Dataspace hdrDataSpace;
Dataspace bestDataSpace = getBestDataspace(displayDevice, &hdrDataSpace);
- if (hdrDataSpace == Dataspace::BT2020_PQ) {
- // Hardware composer can handle BT2100 ColorMode only when
- // - colorimetrical tone mapping is supported, or
- // - Auto mode is turned on and enhanced tone mapping is supported.
- if (displayDevice->hasBT2100PQColorimetricSupport() ||
- (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
- displayDevice->hasBT2100PQEnhanceSupport())) {
- *outMode = ColorMode::BT2100_PQ;
- *outDataSpace = Dataspace::BT2020_PQ;
- } else if (displayDevice->hasHDR10Support()) {
- // Legacy HDR support. HDR layers are treated as UNKNOWN layers.
- hdrDataSpace = Dataspace::UNKNOWN;
- } else {
- // Simulate PQ through RenderEngine, pick DISPLAY_P3 color mode.
- *outMode = ColorMode::DISPLAY_P3;
- *outDataSpace = Dataspace::DISPLAY_P3;
- }
- } else if (hdrDataSpace == Dataspace::BT2020_HLG) {
- if (displayDevice->hasBT2100HLGColorimetricSupport() ||
- (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
- displayDevice->hasBT2100HLGEnhanceSupport())) {
- *outMode = ColorMode::BT2100_HLG;
- *outDataSpace = Dataspace::BT2020_HLG;
- } else if (displayDevice->hasHLGSupport()) {
- // Legacy HDR support. HDR layers are treated as UNKNOWN layers.
- hdrDataSpace = Dataspace::UNKNOWN;
- } else {
- // Simulate HLG through RenderEngine, pick DISPLAY_P3 color mode.
- *outMode = ColorMode::DISPLAY_P3;
- *outDataSpace = Dataspace::DISPLAY_P3;
- }
+ // respect hdrDataSpace only when there is modern HDR support
+ const bool isHdr = hdrDataSpace != Dataspace::UNKNOWN &&
+ displayDevice->hasModernHdrSupport(hdrDataSpace);
+ if (isHdr) {
+ bestDataSpace = hdrDataSpace;
}
- // At this point, there's no HDR layer.
- if (hdrDataSpace == Dataspace::UNKNOWN) {
- switch (bestDataSpace) {
- case Dataspace::DISPLAY_P3:
- case Dataspace::V0_SCRGB_LINEAR:
- *outMode = ColorMode::DISPLAY_P3;
- *outDataSpace = Dataspace::DISPLAY_P3;
- break;
- default:
- *outMode = ColorMode::SRGB;
- *outDataSpace = Dataspace::SRGB;
- break;
- }
- }
- *outRenderIntent = pickRenderIntent(displayDevice, *outMode);
-}
-
-RenderIntent SurfaceFlinger::pickRenderIntent(const sp<DisplayDevice>& displayDevice,
- ColorMode colorMode) const {
- // Native Mode means the display is not color managed, and whichever
- // render intent is picked doesn't matter, thus return
- // RenderIntent::COLORIMETRIC as default here.
- if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) {
- return RenderIntent::COLORIMETRIC;
+ RenderIntent intent;
+ switch (mDisplayColorSetting) {
+ case DisplayColorSetting::MANAGED:
+ case DisplayColorSetting::UNMANAGED:
+ intent = isHdr ? RenderIntent::TONE_MAP_COLORIMETRIC : RenderIntent::COLORIMETRIC;
+ break;
+ case DisplayColorSetting::ENHANCED:
+ intent = isHdr ? RenderIntent::TONE_MAP_ENHANCE : RenderIntent::ENHANCE;
+ break;
+ default: // vendor display color setting
+ intent = static_cast<RenderIntent>(mDisplayColorSetting);
+ break;
}
- // In Auto Color Mode, we want to strech to panel color space, right now
- // only the built-in display supports it.
- if (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
- mBuiltinDisplaySupportsEnhance &&
- displayDevice->getDisplayType() == DisplayDevice::DISPLAY_PRIMARY) {
- switch (colorMode) {
- case ColorMode::DISPLAY_P3:
- case ColorMode::SRGB:
- return RenderIntent::ENHANCE;
- // In Auto Color Mode, BT2100_PQ and BT2100_HLG will only be picked
- // when TONE_MAP_ENHANCE or TONE_MAP_COLORIMETRIC is supported.
- // If TONE_MAP_ENHANCE is not supported, fall back to TONE_MAP_COLORIMETRIC.
- case ColorMode::BT2100_PQ:
- return displayDevice->hasBT2100PQEnhanceSupport() ?
- RenderIntent::TONE_MAP_ENHANCE : RenderIntent::TONE_MAP_COLORIMETRIC;
- case ColorMode::BT2100_HLG:
- return displayDevice->hasBT2100HLGEnhanceSupport() ?
- RenderIntent::TONE_MAP_ENHANCE : RenderIntent::TONE_MAP_COLORIMETRIC;
- // This statement shouldn't be reached, switch cases will always
- // cover all possible ColorMode returned by pickColorMode.
- default:
- return RenderIntent::COLORIMETRIC;
- }
- }
-
- // Either enhance is not supported or we are in natural mode.
-
- // Natural Mode means it's color managed and the color must be right,
- // thus we pick RenderIntent::COLORIMETRIC as render intent for non-HDR
- // content and pick RenderIntent::TONE_MAP_COLORIMETRIC for HDR content.
- switch (colorMode) {
- // In Natural Color Mode, BT2100_PQ and BT2100_HLG will only be picked
- // when TONE_MAP_COLORIMETRIC is supported.
- case ColorMode::BT2100_PQ:
- case ColorMode::BT2100_HLG:
- return RenderIntent::TONE_MAP_COLORIMETRIC;
- default:
- return RenderIntent::COLORIMETRIC;
- }
+ displayDevice->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
}
void SurfaceFlinger::setUpHWComposer() {
@@ -2324,7 +2265,7 @@
const wp<IBinder>& display, int hwcId, const DisplayDeviceState& state,
const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer) {
bool hasWideColorGamut = false;
- std::unordered_map<ColorMode, std::vector<RenderIntent>> hdrAndRenderIntents;
+ std::unordered_map<ColorMode, std::vector<RenderIntent>> hwcColorModes;
if (hasWideColorDisplay) {
std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId);
@@ -2341,18 +2282,7 @@
std::vector<RenderIntent> renderIntents = getHwComposer().getRenderIntents(hwcId,
colorMode);
- if (state.type == DisplayDevice::DISPLAY_PRIMARY) {
- for (auto intent : renderIntents) {
- if (intent == RenderIntent::ENHANCE) {
- mBuiltinDisplaySupportsEnhance = true;
- break;
- }
- }
- }
-
- if (colorMode == ColorMode::BT2100_PQ || colorMode == ColorMode::BT2100_HLG) {
- hdrAndRenderIntents.emplace(colorMode, renderIntents);
- }
+ hwcColorModes.emplace(colorMode, renderIntents);
}
}
@@ -2392,7 +2322,7 @@
dispSurface, std::move(renderSurface), displayWidth, displayHeight,
hasWideColorGamut, hdrCapabilities,
getHwComposer().getSupportedPerFrameMetadata(hwcId),
- hdrAndRenderIntents, initialPowerMode);
+ hwcColorModes, initialPowerMode);
if (maxFrameBufferAcquiredBuffers >= 3) {
nativeWindowSurface->preallocateBuffers();
@@ -2402,10 +2332,13 @@
Dataspace defaultDataSpace = Dataspace::UNKNOWN;
if (hasWideColorGamut) {
defaultColorMode = ColorMode::SRGB;
- defaultDataSpace = Dataspace::V0_SRGB;
+ defaultDataSpace = Dataspace::SRGB;
}
setActiveColorModeInternal(hw, defaultColorMode, defaultDataSpace,
RenderIntent::COLORIMETRIC);
+ if (state.type < DisplayDevice::DISPLAY_VIRTUAL) {
+ hw->setActiveConfig(getHwComposer().getActiveConfigIndex(state.type));
+ }
hw->setLayerStack(state.layerStack);
hw->setProjection(state.orientation, state.viewport, state.frame);
hw->setDisplayName(state.displayName);
@@ -2963,10 +2896,8 @@
ATRACE_INT("hasClientComposition", hasClientComposition);
bool applyColorMatrix = false;
- bool applyLegacyColorMatrix = false;
- mat4 colorMatrix;
- mat4 legacyColorMatrix;
- const mat4* currentColorMatrix = nullptr;
+ bool needsLegacyColorMatrix = false;
+ bool legacyColorMatrixApplied = false;
if (hasClientComposition) {
ALOGV("hasClientComposition");
@@ -2985,19 +2916,13 @@
applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
if (applyColorMatrix) {
- colorMatrix = mDrawingState.colorMatrix;
+ getRenderEngine().setupColorTransform(mDrawingState.colorMatrix);
}
- applyLegacyColorMatrix = (mDisplayColorSetting == DisplayColorSetting::ENHANCED &&
- outputDataspace != Dataspace::UNKNOWN &&
- outputDataspace != Dataspace::SRGB);
- if (applyLegacyColorMatrix) {
- if (applyColorMatrix) {
- legacyColorMatrix = colorMatrix * mLegacySrgbSaturationMatrix;
- } else {
- legacyColorMatrix = mLegacySrgbSaturationMatrix;
- }
- }
+ needsLegacyColorMatrix =
+ (displayDevice->getActiveRenderIntent() >= RenderIntent::ENHANCE &&
+ outputDataspace != Dataspace::UNKNOWN &&
+ outputDataspace != Dataspace::SRGB);
if (!displayDevice->makeCurrent()) {
ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
@@ -3085,18 +3010,14 @@
}
case HWC2::Composition::Client: {
// switch color matrices lazily
- if (layer->isLegacyDataSpace()) {
- if (applyLegacyColorMatrix && currentColorMatrix != &legacyColorMatrix) {
- // TODO(b/78891890) Legacy sRGB saturation matrix should be set
- // separately.
- getRenderEngine().setupColorTransform(legacyColorMatrix);
- currentColorMatrix = &legacyColorMatrix;
+ if (layer->isLegacyDataSpace() && needsLegacyColorMatrix) {
+ if (!legacyColorMatrixApplied) {
+ getRenderEngine().setSaturationMatrix(mLegacySrgbSaturationMatrix);
+ legacyColorMatrixApplied = true;
}
- } else {
- if (applyColorMatrix && currentColorMatrix != &colorMatrix) {
- getRenderEngine().setupColorTransform(colorMatrix);
- currentColorMatrix = &colorMatrix;
- }
+ } else if (legacyColorMatrixApplied) {
+ getRenderEngine().setSaturationMatrix(mat4());
+ legacyColorMatrixApplied = false;
}
layer->draw(renderArea, clip);
@@ -3111,9 +3032,12 @@
firstLayer = false;
}
- if (applyColorMatrix || applyLegacyColorMatrix) {
+ if (applyColorMatrix) {
getRenderEngine().setupColorTransform(mat4());
}
+ if (needsLegacyColorMatrix && legacyColorMatrixApplied) {
+ getRenderEngine().setSaturationMatrix(mat4());
+ }
// disable scissor at the end of the frame
getBE().mRenderEngine->disableScissor();
@@ -3150,12 +3074,14 @@
parent->addChild(lbc);
}
- mGraphicBufferProducerList.insert(IInterface::asBinder(gbc).get());
- // TODO (b/74616334): Change this back to a fatal assert once the leak is fixed
- ALOGE_IF(mGraphicBufferProducerList.size() > mMaxGraphicBufferProducerListSize,
- "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
- mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
- mNumLayers);
+ if (gbc != nullptr) {
+ mGraphicBufferProducerList.insert(IInterface::asBinder(gbc).get());
+ LOG_ALWAYS_FATAL_IF(mGraphicBufferProducerList.size() >
+ mMaxGraphicBufferProducerListSize,
+ "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
+ mGraphicBufferProducerList.size(),
+ mMaxGraphicBufferProducerListSize, mNumLayers);
+ }
mLayersAdded = true;
mNumLayers++;
}
@@ -3419,6 +3345,13 @@
const uint32_t what = s.what;
bool geometryAppliesWithResize =
what & layer_state_t::eGeometryAppliesWithResize;
+
+ // If we are deferring transaction, make sure to push the pending state, as otherwise the
+ // pending state will also be deferred.
+ if (what & layer_state_t::eDeferTransaction) {
+ layer->pushPendingState();
+ }
+
if (what & layer_state_t::ePositionChanged) {
if (layer->setPosition(s.x, s.y, !geometryAppliesWithResize)) {
flags |= eTraversalNeeded;
@@ -4168,7 +4101,8 @@
void SurfaceFlinger::dumpWideColorInfo(String8& result) const {
result.appendFormat("hasWideColorDisplay: %d\n", hasWideColorDisplay);
- result.appendFormat("DisplayColorSetting: %d\n", mDisplayColorSetting);
+ result.appendFormat("DisplayColorSetting: %s\n",
+ decodeDisplayColorSetting(mDisplayColorSetting).c_str());
// TODO: print out if wide-color mode is active or not
@@ -4696,15 +4630,7 @@
return NO_ERROR;
}
case 1023: { // Set native mode
- int32_t value = data.readInt32();
- if (value > 2) {
- return BAD_VALUE;
- }
- if (value == 2 && !mBuiltinDisplaySupportsEnhance) {
- return BAD_VALUE;
- }
-
- mDisplayColorSetting = toDisplayColorSetting(value);
+ mDisplayColorSetting = static_cast<DisplayColorSetting>(data.readInt32());
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
@@ -4733,20 +4659,27 @@
}
// Is a DisplayColorSetting supported?
case 1027: {
- int32_t value = data.readInt32();
- switch (value) {
- case 0:
- reply->writeBool(hasWideColorDisplay);
- return NO_ERROR;
- case 1:
- reply->writeBool(true);
- return NO_ERROR;
- case 2:
- reply->writeBool(mBuiltinDisplaySupportsEnhance);
- return NO_ERROR;
- default:
- return BAD_VALUE;
+ sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+ if (!hw) {
+ return NAME_NOT_FOUND;
}
+
+ DisplayColorSetting setting = static_cast<DisplayColorSetting>(data.readInt32());
+ switch (setting) {
+ case DisplayColorSetting::MANAGED:
+ reply->writeBool(hasWideColorDisplay);
+ break;
+ case DisplayColorSetting::UNMANAGED:
+ reply->writeBool(true);
+ break;
+ case DisplayColorSetting::ENHANCED:
+ reply->writeBool(hw->hasRenderIntent(RenderIntent::ENHANCE));
+ break;
+ default: // vendor display color setting
+ reply->writeBool(hw->hasRenderIntent(static_cast<RenderIntent>(setting)));
+ break;
+ }
+ return NO_ERROR;
}
}
}
@@ -4820,12 +4753,6 @@
return mCrop;
}
}
- bool getWideColorSupport() const override { return false; }
- Dataspace getDataSpace() const override { return Dataspace::UNKNOWN; }
- float getDisplayMaxLuminance() const override {
- return DisplayDevice::sDefaultMaxLumiance;
- }
-
class ReparentForDrawing {
public:
const sp<Layer>& oldParent;
@@ -4916,7 +4843,7 @@
bool useIdentityTransform) {
ATRACE_CALL();
- renderArea.updateDimensions();
+ renderArea.updateDimensions(mPrimaryDisplayOrientation);
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
@@ -5000,13 +4927,35 @@
const auto reqHeight = renderArea.getReqHeight();
Rect sourceCrop = renderArea.getSourceCrop();
- const bool filtering = static_cast<int32_t>(reqWidth) != raWidth ||
- static_cast<int32_t>(reqHeight) != raHeight;
+ bool filtering = false;
+ if (mPrimaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
+ filtering = static_cast<int32_t>(reqWidth) != raHeight ||
+ static_cast<int32_t>(reqHeight) != raWidth;
+ } else {
+ filtering = static_cast<int32_t>(reqWidth) != raWidth ||
+ static_cast<int32_t>(reqHeight) != raHeight;
+ }
// if a default or invalid sourceCrop is passed in, set reasonable values
if (sourceCrop.width() == 0 || sourceCrop.height() == 0 || !sourceCrop.isValid()) {
sourceCrop.setLeftTop(Point(0, 0));
sourceCrop.setRightBottom(Point(raWidth, raHeight));
+ } else if (mPrimaryDisplayOrientation != DisplayState::eOrientationDefault) {
+ Transform tr;
+ uint32_t flags = 0x00;
+ switch (mPrimaryDisplayOrientation) {
+ case DisplayState::eOrientation90:
+ flags = Transform::ROT_90;
+ break;
+ case DisplayState::eOrientation180:
+ flags = Transform::ROT_180;
+ break;
+ case DisplayState::eOrientation270:
+ flags = Transform::ROT_270;
+ break;
+ }
+ tr.set(flags, raWidth, raHeight);
+ sourceCrop = tr.transform(sourceCrop);
}
// ensure that sourceCrop is inside screen
@@ -5023,19 +4972,47 @@
ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, raHeight);
}
- Dataspace outputDataspace = Dataspace::UNKNOWN;
- if (renderArea.getWideColorSupport()) {
- outputDataspace = renderArea.getDataSpace();
- }
- engine.setOutputDataSpace(outputDataspace);
- engine.setDisplayMaxLuminance(renderArea.getDisplayMaxLuminance());
+ // assume ColorMode::SRGB / RenderIntent::COLORIMETRIC
+ engine.setOutputDataSpace(Dataspace::SRGB);
+ engine.setDisplayMaxLuminance(DisplayDevice::sDefaultMaxLumiance);
// make sure to clear all GL error flags
engine.checkErrors();
+ Transform::orientation_flags rotation = renderArea.getRotationFlags();
+ if (mPrimaryDisplayOrientation != DisplayState::eOrientationDefault) {
+ // convert hw orientation into flag presentation
+ // here inverse transform needed
+ uint8_t hw_rot_90 = 0x00;
+ uint8_t hw_flip_hv = 0x00;
+ switch (mPrimaryDisplayOrientation) {
+ case DisplayState::eOrientation90:
+ hw_rot_90 = Transform::ROT_90;
+ hw_flip_hv = Transform::ROT_180;
+ break;
+ case DisplayState::eOrientation180:
+ hw_flip_hv = Transform::ROT_180;
+ break;
+ case DisplayState::eOrientation270:
+ hw_rot_90 = Transform::ROT_90;
+ break;
+ }
+
+ // transform flags operation
+ // 1) flip H V if both have ROT_90 flag
+ // 2) XOR these flags
+ uint8_t rotation_rot_90 = rotation & Transform::ROT_90;
+ uint8_t rotation_flip_hv = rotation & Transform::ROT_180;
+ if (rotation_rot_90 & hw_rot_90) {
+ rotation_flip_hv = (~rotation_flip_hv) & Transform::ROT_180;
+ }
+ rotation = static_cast<Transform::orientation_flags>
+ ((rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv));
+ }
+
// set-up our viewport
engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, raHeight, yswap,
- renderArea.getRotationFlags());
+ rotation);
engine.disableTexturing();
const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d9cf946..8566b03 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -341,6 +341,8 @@
bool authenticateSurfaceTextureLocked(
const sp<IGraphicBufferProducer>& bufferProducer) const;
+ int getPrimaryDisplayOrientation() const { return mPrimaryDisplayOrientation; }
+
private:
friend class Client;
friend class DisplayEventConnection;
@@ -367,7 +369,7 @@
class State {
public:
- explicit State(LayerVector::StateSet set) : stateSet(set) {}
+ explicit State(LayerVector::StateSet set) : stateSet(set), layersSortedByZ(set) {}
State& operator=(const State& other) {
// We explicitly don't copy stateSet so that, e.g., mDrawingState
// always uses the Drawing StateSet.
@@ -664,8 +666,6 @@
ui::ColorMode* outMode,
ui::Dataspace* outDataSpace,
ui::RenderIntent* outRenderIntent) const;
- ui::RenderIntent pickRenderIntent(const sp<DisplayDevice>& displayDevice,
- ui::ColorMode colorMode) const;
void setUpHWComposer();
void doComposition();
@@ -845,6 +845,7 @@
mutable std::unique_ptr<MessageQueue> mEventQueue{std::make_unique<impl::MessageQueue>()};
FrameTracker mAnimFrameTracker;
DispSync mPrimaryDispSync;
+ int mPrimaryDisplayOrientation = DisplayState::eOrientationDefault;
// protected by mDestroyedLayerLock;
mutable Mutex mDestroyedLayerLock;
@@ -880,7 +881,6 @@
DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::MANAGED;
// Applied on sRGB layers when the render intent is non-colorimetric.
mat4 mLegacySrgbSaturationMatrix;
- bool mBuiltinDisplaySupportsEnhance = false;
using CreateBufferQueueFunction =
std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
diff --git a/services/surfaceflinger/VSyncModulator.h b/services/surfaceflinger/VSyncModulator.h
index 3e5800e..d526313 100644
--- a/services/surfaceflinger/VSyncModulator.h
+++ b/services/surfaceflinger/VSyncModulator.h
@@ -28,6 +28,12 @@
* Modulates the vsync-offsets depending on current SurfaceFlinger state.
*/
class VSyncModulator {
+private:
+
+ // Number of frames we'll keep the early phase offsets once they are activated. This acts as a
+ // low-pass filter in case the client isn't quick enough in sending new transactions.
+ const int MIN_EARLY_FRAME_COUNT = 2;
+
public:
enum TransactionStart {
@@ -55,6 +61,11 @@
}
void setTransactionStart(TransactionStart transactionStart) {
+
+ if (transactionStart == TransactionStart::EARLY) {
+ mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT;
+ }
+
// An early transaction stays an early transaction.
if (transactionStart == mTransactionStart || mTransactionStart == TransactionStart::EARLY) {
return;
@@ -69,10 +80,19 @@
updatePhaseOffsets();
}
- void setLastFrameUsedRenderEngine(bool re) {
- if (re == mLastFrameUsedRenderEngine) return;
- mLastFrameUsedRenderEngine = re;
- updatePhaseOffsets();
+ void onRefreshed(bool usedRenderEngine) {
+ bool updatePhaseOffsetsNeeded = false;
+ if (mRemainingEarlyFrameCount > 0) {
+ mRemainingEarlyFrameCount--;
+ updatePhaseOffsetsNeeded = true;
+ }
+ if (usedRenderEngine != mLastFrameUsedRenderEngine) {
+ mLastFrameUsedRenderEngine = usedRenderEngine;
+ updatePhaseOffsetsNeeded = true;
+ }
+ if (updatePhaseOffsetsNeeded) {
+ updatePhaseOffsets();
+ }
}
private:
@@ -82,7 +102,7 @@
// Do not change phase offsets if disabled.
if (mEarlyPhaseOffset == mLatePhaseOffset) return;
- if (mTransactionStart == TransactionStart::EARLY || mLastFrameUsedRenderEngine) {
+ if (shouldUseEarlyOffset()) {
if (mPhaseOffset != mEarlyPhaseOffset) {
if (mEventThread) {
mEventThread->setPhaseOffset(mEarlyPhaseOffset);
@@ -99,12 +119,18 @@
}
}
+ bool shouldUseEarlyOffset() {
+ return mTransactionStart == TransactionStart::EARLY || mLastFrameUsedRenderEngine
+ || mRemainingEarlyFrameCount > 0;
+ }
+
nsecs_t mLatePhaseOffset = 0;
nsecs_t mEarlyPhaseOffset = 0;
EventThread* mEventThread = nullptr;
std::atomic<nsecs_t> mPhaseOffset = 0;
std::atomic<TransactionStart> mTransactionStart = TransactionStart::NORMAL;
std::atomic<bool> mLastFrameUsedRenderEngine = false;
+ std::atomic<int> mRemainingEarlyFrameCount = 0;
};
} // namespace android
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 2a924ae..8255b41 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -35,6 +35,13 @@
using namespace android;
static status_t startGraphicsAllocatorService() {
+ using android::hardware::configstore::getBool;
+ using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs;
+ if (!getBool<ISurfaceFlingerConfigs,
+ &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(false)) {
+ return OK;
+ }
+
using android::hardware::graphics::allocator::V2_0::IAllocator;
status_t result =
@@ -47,27 +54,12 @@
return OK;
}
-static status_t startHidlServices() {
+static status_t startDisplayService() {
using android::frameworks::displayservice::V1_0::implementation::DisplayService;
using android::frameworks::displayservice::V1_0::IDisplayService;
- using android::hardware::configstore::getBool;
- using android::hardware::configstore::getBool;
- using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs;
- hardware::configureRpcThreadpool(1 /* maxThreads */,
- false /* callerWillJoin */);
-
- status_t err;
-
- if (getBool<ISurfaceFlingerConfigs,
- &ISurfaceFlingerConfigs::startGraphicsAllocatorService>(false)) {
- err = startGraphicsAllocatorService();
- if (err != OK) {
- return err;
- }
- }
sp<IDisplayService> displayservice = new DisplayService();
- err = displayservice->registerAsService();
+ status_t err = displayservice->registerAsService();
if (err != OK) {
ALOGE("Could not register IDisplayService service.");
@@ -77,9 +69,13 @@
}
int main(int, char**) {
- startHidlServices();
-
signal(SIGPIPE, SIG_IGN);
+
+ hardware::configureRpcThreadpool(1 /* maxThreads */,
+ false /* callerWillJoin */);
+
+ startGraphicsAllocatorService();
+
// When SF is launched in its own process, limit the number of
// binder threads to 4.
ProcessState::self()->setThreadPoolMaxThreadCount(4);
@@ -112,6 +108,8 @@
sp<GpuService> gpuservice = new GpuService();
sm->addService(String16(GpuService::SERVICE_NAME), gpuservice, false);
+ startDisplayService(); // dependency on SF getting registered above
+
struct sched_param param = {0};
param.sched_priority = 2;
if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) {
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index bcabe0d..39761dd 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -20,6 +20,8 @@
srcs: [
":libsurfaceflinger_sources",
"DisplayTransactionTest.cpp",
+ "EventControlThreadTest.cpp",
+ "EventThreadTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplaySurface.cpp",
"mock/gui/MockGraphicBufferConsumer.cpp",
diff --git a/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
new file mode 100644
index 0000000..2245ee1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/AsyncCallRecorder.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2018 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 <chrono>
+#include <deque>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+
+// This class helps record calls made by another thread when they are made
+// asynchronously, with no other way for the tests to verify that the calls have
+// been made.
+//
+// A normal Google Mock recorder, while thread safe, does not allow you to wait
+// for asynchronous calls to be made.
+//
+// Usage:
+//
+// In the test, use a Google Mock expectation to invoke an instance of the
+// recorder:
+//
+// AsyncCallRecorder<void(int)> recorder;
+//
+// EXPECT_CALL(someMock, someFunction(_)).
+// .WillRepeatedly(Invoke(recorder.getInvocable()));
+//
+// Then you can invoke the functionality being tested:
+//
+// threadUnderTest.doSomethingAsync()
+//
+// And afterwards make a number of assertions using the recorder:
+//
+// // Wait for one call (with reasonable default timeout), and get the args
+// // as a std::tuple inside a std::optional.
+// auto args = recorder.waitForCall();
+// // The returned std::optional will have a value if the recorder function
+// // was called.
+// ASSERT_TRUE(args.has_value());
+// // The arguments can be checked if needed using standard tuple
+// // operations.
+// EXPECT_EQ(123, std::get<0>(args.value()));
+//
+// Alternatively maybe you want to assert that a call was not made.
+//
+// EXPECT_FALSE(recorder.waitForUnexpectedCall().has_value());
+//
+// However this check uses a really short timeout so as not to block the test
+// unnecessarily. And it could be possible for the check to return false and
+// then the recorder could observe a call being made after.
+template <typename Func>
+class AsyncCallRecorder;
+
+template <typename... Args>
+class AsyncCallRecorder<void (*)(Args...)> {
+public:
+ // For the tests, we expect the wait for an expected change to be signaled
+ // to be much shorter than this.
+ static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{10};
+
+ // The wait here is tricky. We don't expect a change, but we don't want to
+ // wait forever (or for longer than the typical test function runtime). As
+ // even the simplest Google Test can take 1ms (1000us) to run, we wait for
+ // half that time.
+ static constexpr std::chrono::microseconds UNEXPECTED_CALL_TIMEOUT{500};
+
+ using ArgTuple = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
+
+ void recordCall(Args... args) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCalls.emplace_back(std::make_tuple(args...));
+ mCondition.notify_all();
+ }
+
+ // Returns a functor which can be used with the Google Mock Invoke()
+ // function, or as a std::function to record calls.
+ auto getInvocable() {
+ return [this](Args... args) { recordCall(args...); };
+ }
+
+ // Returns a set of arguments as a std::optional<std::tuple<...>> for the
+ // oldest call, waiting for the given timeout if necessary if there are no
+ // arguments in the FIFO.
+ std::optional<ArgTuple> waitForCall(
+ std::chrono::microseconds timeout = DEFAULT_CALL_EXPECTED_TIMEOUT)
+ NO_THREAD_SAFETY_ANALYSIS {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ // Wait if necessary for us to have a record from a call.
+ mCondition.wait_for(lock, timeout,
+ [this]() NO_THREAD_SAFETY_ANALYSIS { return !mCalls.empty(); });
+
+ // Return the arguments from the oldest call, if one was made
+ bool called = !mCalls.empty();
+ std::optional<ArgTuple> result;
+ if (called) {
+ result.emplace(std::move(mCalls.front()));
+ mCalls.pop_front();
+ }
+ return result;
+ }
+
+ // Waits using a small default timeout for when a call is not expected to be
+ // made. The returned std::optional<std:tuple<...>> should not have a value
+ // except if a set of arguments was unexpectedly received because a call was
+ // actually made.
+ //
+ // Note this function uses a small timeout to not block test execution, and
+ // it is possible the code under test could make the call AFTER the timeout
+ // expires.
+ std::optional<ArgTuple> waitForUnexpectedCall() { return waitForCall(UNEXPECTED_CALL_TIMEOUT); }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ std::deque<ArgTuple> mCalls GUARDED_BY(mMutex);
+};
+
+// Like AsyncCallRecorder, but for when the function being invoked
+// asynchronously is expected to return a value.
+//
+// This helper allows a single constant return value to be set to be returned by
+// all calls that were made.
+template <typename Func>
+class AsyncCallRecorderWithCannedReturn;
+
+template <typename Ret, typename... Args>
+class AsyncCallRecorderWithCannedReturn<Ret (*)(Args...)>
+ : public AsyncCallRecorder<void (*)(Args...)> {
+public:
+ explicit AsyncCallRecorderWithCannedReturn(Ret returnvalue) : mReturnValue(returnvalue) {}
+
+ auto getInvocable() {
+ return [this](Args... args) {
+ this->recordCall(args...);
+ return mReturnValue;
+ };
+ }
+
+private:
+ const Ret mReturnValue;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 08da1a2..9b308bf 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -45,12 +45,13 @@
using testing::Return;
using testing::SetArgPointee;
-using android::hardware::graphics::common::V1_0::Hdr;
-using android::hardware::graphics::common::V1_1::ColorMode;
-using android::hardware::graphics::common::V1_1::RenderIntent;
+using android::Hwc2::ColorMode;
using android::Hwc2::Error;
+using android::Hwc2::Hdr;
using android::Hwc2::IComposer;
using android::Hwc2::IComposerClient;
+using android::Hwc2::PerFrameMetadataKey;
+using android::Hwc2::RenderIntent;
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
@@ -61,6 +62,8 @@
constexpr int32_t DEFAULT_DPI = 320;
constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
+constexpr int HWC_POWER_MODE_LEET = 1337; // An out of range power mode value
+
/* ------------------------------------------------------------------------
* Boolean avoidance
*
@@ -303,11 +306,7 @@
static constexpr HWC2::DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
// The HWC active configuration id
- // TODO(b/69807179): SurfaceFlinger does not correctly get the active
- // config. Once it does, change this to non-zero so that it is properly
- // covered.
- // static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
- static constexpr int HWC_ACTIVE_CONFIG_ID = 0;
+ static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
static void injectPendingHotplugEvent(DisplayTransactionTest* test,
HWC2::Connection connection) {
@@ -359,13 +358,11 @@
// Called by tests to set up HWC call expectations
static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
}
};
struct NonHwcDisplayVariant {
- static constexpr int HWC_ACTIVE_CONFIG_ID = 0;
-
static void injectHwcDisplay(DisplayTransactionTest*) {}
static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
@@ -508,10 +505,7 @@
static void setupComposerCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
.WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- setColorMode(Display::HWC_DISPLAY_ID, ColorMode::NATIVE,
- RenderIntent::COLORIMETRIC))
- .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
}
};
@@ -581,48 +575,118 @@
}
};
+struct NonHwcPerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = 0;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_, _)).Times(0);
+ }
+};
+
+template <typename Display>
+struct NoPerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = 0;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>()),
+ Return(Error::NONE)));
+ }
+};
+
+template <typename Display>
+struct Smpte2086PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
+ PerFrameMetadataKey::WHITE_POINT_X,
+ PerFrameMetadataKey::WHITE_POINT_Y,
+ PerFrameMetadataKey::MAX_LUMINANCE,
+ PerFrameMetadataKey::MIN_LUMINANCE,
+ })),
+ Return(Error::NONE)));
+ }
+};
+
+template <typename Display>
+struct Cta861_3_PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
+ PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+ })),
+ Return(Error::NONE)));
+ }
+};
+
/* ------------------------------------------------------------------------
* Typical display configurations to test
*/
-template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy>
+template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy,
+ typename PerFrameMetadataSupportPolicy>
struct Case {
using Display = DisplayPolicy;
using WideColorSupport = WideColorSupportPolicy;
using HdrSupport = HdrSupportPolicy;
+ using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy;
};
using SimplePrimaryDisplayCase =
Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>>;
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
using SimpleExternalDisplayCase =
Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>,
- HdrNotSupportedVariant<ExternalDisplayVariant>>;
+ HdrNotSupportedVariant<ExternalDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>;
using SimpleTertiaryDisplayCase =
Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>,
- HdrNotSupportedVariant<TertiaryDisplayVariant>>;
+ HdrNotSupportedVariant<TertiaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>;
using NonHwcVirtualDisplayCase =
Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>,
- WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant>;
+ WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant,
+ NonHwcPerFrameMetadataSupportVariant>;
using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>;
using HwcVirtualDisplayCase =
Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant,
- HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>>;
+ HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>;
using WideColorP3ColorimetricDisplayCase =
Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>>;
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
using Hdr10DisplayCase =
Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- Hdr10SupportedVariant<PrimaryDisplayVariant>>;
+ Hdr10SupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
using HdrHlgDisplayCase =
Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrHlgSupportedVariant<PrimaryDisplayVariant>>;
+ HdrHlgSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
using HdrDolbyVisionDisplayCase =
Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>>;
+ HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrSmpte2086DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrCta861_3_DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
using InvalidDisplayCase = Case<InvalidDisplayVariant, WideColorSupportNotConfiguredVariant,
- NonHwcDisplayHdrSupportVariant>;
-
+ NonHwcDisplayHdrSupportVariant,
+ NoPerFrameMetadataSupportVariant<InvalidDisplayVariant>>;
/* ------------------------------------------------------------------------
*
* SurfaceFlinger::onHotplugReceived
@@ -960,12 +1024,10 @@
// Various native window calls will be made.
Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
-
- // TODO(b/69807179): SurfaceFlinger does not correctly get the active config.
- // Case::Display::setupHwcGetActiveConfigCallExpectations(this)
-
+ Case::Display::setupHwcGetActiveConfigCallExpectations(this);
Case::WideColorSupport::setupComposerCallExpectations(this);
Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
// --------------------------------------------------------------------
// Invocation
@@ -986,7 +1048,12 @@
EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
- EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveConfig());
+ // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
+ // remapped, and the test only ever sets up one config. If there were an error
+ // looking up the remapped index, device->getActiveConfig() would be -1 instead.
+ EXPECT_EQ(0, device->getActiveConfig());
+ EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
+ device->getSupportedPerFrameMetadata());
}
TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) {
@@ -1025,6 +1092,14 @@
setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>();
}
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) {
+ setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) {
+ setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>();
+}
+
/* ------------------------------------------------------------------------
* SurfaceFlinger::handleTransactionLocked(eDisplayTransactionNeeded)
*/
@@ -1082,6 +1157,7 @@
Case::WideColorSupport::setupComposerCallExpectations(this);
Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, true)).Times(1);
@@ -1421,6 +1497,7 @@
Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
Case::WideColorSupport::setupComposerCallExpectations(this);
Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
// --------------------------------------------------------------------
// Invocation
@@ -2276,5 +2353,446 @@
EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency);
}
+/* ------------------------------------------------------------------------
+ * SurfaceFlinger::setPowerModeInternal
+ */
+
+// Used when we simulate a display that supports doze.
+struct DozeIsSupportedVariant {
+ static constexpr bool DOZE_SUPPORTED = true;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+ IComposerClient::PowerMode::DOZE;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+ IComposerClient::PowerMode::DOZE_SUSPEND;
+};
+
+// Used when we simulate a display that does not support doze.
+struct DozeNotSupportedVariant {
+ static constexpr bool DOZE_SUPPORTED = false;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+ IComposerClient::PowerMode::ON;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+ IComposerClient::PowerMode::ON;
+};
+
+struct EventThreadBaseSupportedVariant {
+ static void setupEventAndEventControlThreadNoCallExpectations(DisplayTransactionTest* test) {
+ // The event control thread should not be notified.
+ EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(_)).Times(0);
+
+ // The event thread should not be notified.
+ EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
+ EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
+ }
+};
+
+struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
+ static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // These calls are only expected for the primary display.
+
+ // Instead expect no calls.
+ setupEventAndEventControlThreadNoCallExpectations(test);
+ }
+
+ static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // These calls are only expected for the primary display.
+
+ // Instead expect no calls.
+ setupEventAndEventControlThreadNoCallExpectations(test);
+ }
+};
+
+struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
+ static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // The event control thread should be notified to enable vsyncs
+ EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(true)).Times(1);
+
+ // The event thread should be notified that the screen was acquired.
+ EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
+ }
+
+ static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // There should be a call to setVsyncEnabled(false)
+ EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(false)).Times(1);
+
+ // The event thread should not be notified that the screen was released.
+ EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
+ }
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// There are a large number of transitions we could test, however we only test a
+// selected subset which provides complete test coverage of the implementation.
+// --------------------------------------------------------------------
+
+template <int initialPowerMode, int targetPowerMode>
+struct TransitionVariantCommon {
+ static constexpr auto INITIAL_POWER_MODE = initialPowerMode;
+ static constexpr auto TARGET_POWER_MODE = targetPowerMode;
+
+ static void verifyPostconditions(DisplayTransactionTest*) {}
+};
+
+struct TransitionOffToOnVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_OFF, HWC_POWER_MODE_NORMAL> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::setupRepaintEverythingCallExpectations(test);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+ }
+};
+
+struct TransitionOffToDozeSuspendVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_OFF, HWC_POWER_MODE_DOZE_SUSPEND> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+ Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::setupRepaintEverythingCallExpectations(test);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+ }
+};
+
+struct TransitionOnToOffVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_OFF> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ }
+};
+
+struct TransitionDozeSuspendToOffVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_OFF> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ }
+};
+
+struct TransitionOnToDozeVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_DOZE> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+ }
+};
+
+struct TransitionDozeSuspendToDozeVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_DOZE> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+ }
+};
+
+struct TransitionDozeToOnVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_DOZE, HWC_POWER_MODE_NORMAL> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ }
+};
+
+struct TransitionDozeSuspendToOnVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_NORMAL> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ }
+};
+
+struct TransitionOnToDozeSuspendVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_DOZE_SUSPEND> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+ }
+};
+
+struct TransitionOnToUnknownVariant
+ : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_LEET> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
+ Case::setupNoComposerPowerModeCallExpectations(test);
+ }
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// Rather than testing the cartesian product of of
+// DozeIsSupported/DozeNotSupported with all other options, we use one for one
+// display type, and the other for another display type.
+// --------------------------------------------------------------------
+
+template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
+ typename TransitionVariant>
+struct DisplayPowerCase {
+ using Display = DisplayVariant;
+ using Doze = DozeVariant;
+ using EventThread = EventThreadVariant;
+ using Transition = TransitionVariant;
+
+ static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, int mode) {
+ Display::injectHwcDisplay(test);
+ auto display = Display::makeFakeExistingDisplayInjector(test);
+ display.inject();
+ display.mutableDisplayDevice()->setPowerMode(mode);
+ return display;
+ }
+
+ static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
+ test->mFlinger.mutablePrimaryHWVsyncEnabled() = enabled;
+ }
+
+ static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
+ }
+
+ static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test, int mode) {
+ EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
+ EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, mode)).Times(1);
+ }
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test,
+ IComposerClient::PowerMode mode) {
+ // Any calls to get the active config will return a default value.
+ EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID),
+ Return(Error::NONE)));
+
+ // Any calls to get whether the display supports dozing will return the value set by the
+ // policy variant.
+ EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE)));
+
+ EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1);
+ }
+
+ static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0);
+ }
+};
+
+// A sample configuration for the primary display.
+// In addition to having event thread support, we emulate doze support.
+template <typename TransitionVariant>
+using PrimaryDisplayPowerCase = DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant,
+ EventThreadIsSupportedVariant, TransitionVariant>;
+
+// A sample configuration for the external display.
+// In addition to not having event thread support, we emulate not having doze
+// support.
+template <typename TransitionVariant>
+using ExternalDisplayPowerCase =
+ DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant,
+ EventThreadNotSupportedVariant, TransitionVariant>;
+
+class SetPowerModeInternalTest : public DisplayTransactionTest {
+public:
+ template <typename Case>
+ void transitionDisplayCommon();
+};
+
+template <int PowerMode>
+struct PowerModeInitialVSyncEnabled : public std::false_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<HWC_POWER_MODE_NORMAL> : public std::true_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<HWC_POWER_MODE_DOZE> : public std::true_type {};
+
+template <typename Case>
+void SetPowerModeInternalTest::transitionDisplayCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ auto display =
+ Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
+ Case::setInitialPrimaryHWVsyncEnabled(this,
+ PowerModeInitialVSyncEnabled<
+ Case::Transition::INITIAL_POWER_MODE>::value);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
+ Case::Transition::template setupCallExpectations<Case>(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
+ Case::Transition::TARGET_POWER_MODE);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ Case::Transition::verifyPostconditions(this);
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A primary display device is set up
+ Case::Display::injectHwcDisplay(this);
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The diplay is already set to HWC_POWER_MODE_NORMAL
+ display.mutableDisplayDevice()->setPowerMode(HWC_POWER_MODE_NORMAL);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_NORMAL);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(HWC_POWER_MODE_NORMAL, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalJustSetsInternalStateIfVirtualDisplay) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // We need to resize this so that the HWC thinks the virtual display
+ // is something it created.
+ mFlinger.mutableHwcDisplayData().resize(3);
+
+ // A virtual display device is set up
+ Case::Display::injectHwcDisplay(this);
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display is set to HWC_POWER_MODE_OFF
+ getDisplayDevice(display.token())->setPowerMode(HWC_POWER_MODE_OFF);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_NORMAL);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(HWC_POWER_MODE_NORMAL, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
new file mode 100644
index 0000000..b346454
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include "AsyncCallRecorder.h"
+#include "EventControlThread.h"
+
+namespace android {
+namespace {
+
+using namespace std::chrono_literals;
+using testing::_;
+
+class EventControlThreadTest : public testing::Test {
+protected:
+ EventControlThreadTest();
+ ~EventControlThreadTest() override;
+
+ void createThread();
+
+ void expectVSyncEnableCallbackCalled(bool enable);
+
+ AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
+
+ std::unique_ptr<EventControlThread> mThread;
+};
+
+EventControlThreadTest::EventControlThreadTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+EventControlThreadTest::~EventControlThreadTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+void EventControlThreadTest::createThread() {
+ mThread = std::make_unique<android::impl::EventControlThread>(
+ mVSyncSetEnabledCallRecorder.getInvocable());
+}
+
+void EventControlThreadTest::expectVSyncEnableCallbackCalled(bool expectedEnabled) {
+ auto args = mVSyncSetEnabledCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(std::get<0>(args.value()), expectedEnabled);
+}
+
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+
+TEST_F(EventControlThreadTest, signalsVSyncDisabledOnStartup) {
+ createThread();
+
+ // On thread start, there should be an automatic explicit call to disable
+ // vsyncs
+ expectVSyncEnableCallbackCalled(false);
+}
+
+TEST_F(EventControlThreadTest, signalsVSyncDisabledOnce) {
+ createThread();
+ expectVSyncEnableCallbackCalled(false);
+
+ mThread->setVsyncEnabled(false);
+
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
+TEST_F(EventControlThreadTest, signalsVSyncEnabledThenDisabled) {
+ createThread();
+ expectVSyncEnableCallbackCalled(false);
+
+ mThread->setVsyncEnabled(true);
+
+ expectVSyncEnableCallbackCalled(true);
+
+ mThread->setVsyncEnabled(false);
+
+ expectVSyncEnableCallbackCalled(false);
+}
+
+TEST_F(EventControlThreadTest, signalsVSyncEnabledOnce) {
+ createThread();
+ expectVSyncEnableCallbackCalled(false);
+
+ mThread->setVsyncEnabled(true);
+
+ expectVSyncEnableCallbackCalled(true);
+
+ mThread->setVsyncEnabled(true);
+
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
new file mode 100644
index 0000000..80fdb80
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+
+#include <utils/Errors.h>
+
+#include "AsyncCallRecorder.h"
+#include "EventThread.h"
+
+using namespace std::chrono_literals;
+using namespace std::placeholders;
+
+using testing::_;
+using testing::Invoke;
+
+namespace android {
+namespace {
+
+class MockVSyncSource : public VSyncSource {
+public:
+ MOCK_METHOD1(setVSyncEnabled, void(bool));
+ MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
+ MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
+};
+
+} // namespace
+
+class EventThreadTest : public testing::Test {
+protected:
+ class MockEventThreadConnection : public android::impl::EventThread::Connection {
+ public:
+ explicit MockEventThreadConnection(android::impl::EventThread* eventThread)
+ : android::impl::EventThread::Connection(eventThread) {}
+ MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
+ };
+
+ using ConnectionEventRecorder =
+ AsyncCallRecorderWithCannedReturn<status_t (*)(const DisplayEventReceiver::Event&)>;
+
+ EventThreadTest();
+ ~EventThreadTest() override;
+
+ void createThread();
+ sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder);
+
+ void expectVSyncSetEnabledCallReceived(bool expectedState);
+ void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset);
+ VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
+ void expectInterceptCallReceived(nsecs_t expectedTimestamp);
+ void expectVsyncEventReceivedByConnection(const char* name,
+ ConnectionEventRecorder& connectionEventRecorder,
+ nsecs_t expectedTimestamp, unsigned expectedCount);
+ void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
+ void expectHotplugEventReceivedByConnection(int expectedDisplayType, bool expectedConnected);
+
+ AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
+ AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
+ AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder;
+ AsyncCallRecorder<void (*)()> mResyncCallRecorder;
+ AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
+ ConnectionEventRecorder mConnectionEventCallRecorder{0};
+
+ MockVSyncSource mVSyncSource;
+ std::unique_ptr<android::impl::EventThread> mThread;
+ sp<MockEventThreadConnection> mConnection;
+};
+
+EventThreadTest::EventThreadTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+ EXPECT_CALL(mVSyncSource, setVSyncEnabled(_))
+ .WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable()));
+
+ EXPECT_CALL(mVSyncSource, setCallback(_))
+ .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable()));
+
+ EXPECT_CALL(mVSyncSource, setPhaseOffset(_))
+ .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
+
+ createThread();
+ mConnection = createConnection(mConnectionEventCallRecorder);
+}
+
+EventThreadTest::~EventThreadTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+void EventThreadTest::createThread() {
+ mThread =
+ std::make_unique<android::impl::EventThread>(&mVSyncSource,
+ mResyncCallRecorder.getInvocable(),
+ mInterceptVSyncCallRecorder.getInvocable(),
+ "unit-test-event-thread");
+}
+
+sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
+ ConnectionEventRecorder& recorder) {
+ sp<MockEventThreadConnection> connection = new MockEventThreadConnection(mThread.get());
+ EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
+ return connection;
+}
+
+void EventThreadTest::expectVSyncSetEnabledCallReceived(bool expectedState) {
+ auto args = mVSyncSetEnabledCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedState, std::get<0>(args.value()));
+}
+
+void EventThreadTest::expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset) {
+ auto args = mVSyncSetPhaseOffsetCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedPhaseOffset, std::get<0>(args.value()));
+}
+
+VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() {
+ auto callbackSet = mVSyncSetCallbackCallRecorder.waitForCall();
+ return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr;
+}
+
+void EventThreadTest::expectInterceptCallReceived(nsecs_t expectedTimestamp) {
+ auto args = mInterceptVSyncCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
+}
+
+void EventThreadTest::expectVsyncEventReceivedByConnection(
+ const char* name, ConnectionEventRecorder& connectionEventRecorder,
+ nsecs_t expectedTimestamp, unsigned expectedCount) {
+ auto args = connectionEventRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value()) << name << " did not receive an event for timestamp "
+ << expectedTimestamp;
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_VSYNC, event.header.type)
+ << name << " did not get the correct event for timestamp " << expectedTimestamp;
+ EXPECT_EQ(expectedTimestamp, event.header.timestamp)
+ << name << " did not get the expected timestamp for timestamp " << expectedTimestamp;
+ EXPECT_EQ(expectedCount, event.vsync.count)
+ << name << " did not get the expected count for timestamp " << expectedTimestamp;
+}
+
+void EventThreadTest::expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp,
+ unsigned expectedCount) {
+ expectVsyncEventReceivedByConnection("mConnectionEventCallRecorder",
+ mConnectionEventCallRecorder, expectedTimestamp,
+ expectedCount);
+}
+
+void EventThreadTest::expectHotplugEventReceivedByConnection(int expectedDisplayType,
+ bool expectedConnected) {
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ const auto& event = std::get<0>(args.value());
+ EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, event.header.type);
+ EXPECT_EQ(static_cast<unsigned>(expectedDisplayType), event.header.id);
+ EXPECT_EQ(expectedConnected, event.hotplug.connected);
+}
+
+namespace {
+
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+
+TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) {
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
+ EXPECT_FALSE(mVSyncSetPhaseOffsetCallRecorder.waitForCall(0us).has_value());
+ EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value());
+ EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
+}
+
+TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
+ // Signal that we want the next vsync event to be posted to the connection
+ mThread->requestNextVsync(mConnection);
+
+ // EventThread should immediately request a resync.
+ EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // Use the received callback to signal a first vsync event.
+ // The interceptor should receive the event, as well as the connection.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ expectVsyncEventReceivedByConnection(123, 1u);
+
+ // Use the received callback to signal a second vsync event.
+ // The interceptor should receive the event, but the the connection should
+ // not as it was only interested in the first.
+ callback->onVSyncEvent(456);
+ expectInterceptCallReceived(456);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // EventThread should also detect that at this point that it does not need
+ // any more vsync events, and should disable their generation.
+ expectVSyncSetEnabledCallReceived(false);
+}
+
+TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
+ // Create a first connection, register it, and request a vsync rate of zero.
+ ConnectionEventRecorder firstConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder);
+ mThread->setVsyncRate(0, firstConnection);
+
+ // By itself, this should not enable vsync events
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
+
+ // However if there is another connection which wants events at a nonzero rate.....
+ ConnectionEventRecorder secondConnectionEventRecorder{0};
+ sp<MockEventThreadConnection> secondConnection =
+ createConnection(secondConnectionEventRecorder);
+ mThread->setVsyncRate(1, secondConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // Send a vsync event. EventThread should then make a call to the
+ // interceptor, and the second connection. The first connection should not
+ // get the event.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
+ expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
+ 1u);
+}
+
+TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) {
+ mThread->setVsyncRate(1, mConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // Send a vsync event. EventThread should then make a call to the
+ // interceptor, and the connection.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ expectVsyncEventReceivedByConnection(123, 1u);
+
+ // A second event should go to the same places.
+ callback->onVSyncEvent(456);
+ expectInterceptCallReceived(456);
+ expectVsyncEventReceivedByConnection(456, 2u);
+
+ // A third event should go to the same places.
+ callback->onVSyncEvent(789);
+ expectInterceptCallReceived(789);
+ expectVsyncEventReceivedByConnection(789, 3u);
+}
+
+TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) {
+ mThread->setVsyncRate(2, mConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // The first event will be seen by the interceptor, and not the connection.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // The second event will be seen by the interceptor and the connection.
+ callback->onVSyncEvent(456);
+ expectInterceptCallReceived(456);
+ expectVsyncEventReceivedByConnection(456, 2u);
+
+ // The third event will be seen by the interceptor, and not the connection.
+ callback->onVSyncEvent(789);
+ expectInterceptCallReceived(789);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // The fourth event will be seen by the interceptor and the connection.
+ callback->onVSyncEvent(101112);
+ expectInterceptCallReceived(101112);
+ expectVsyncEventReceivedByConnection(101112, 4u);
+}
+
+TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) {
+ mThread->setVsyncRate(1, mConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // Destroy the only (strong) reference to the connection.
+ mConnection = nullptr;
+
+ // The first event will be seen by the interceptor, and not the connection.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // EventThread should disable vsync callbacks
+ expectVSyncSetEnabledCallReceived(false);
+}
+
+TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
+ ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
+ sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
+ mThread->setVsyncRate(1, errorConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // The first event will be seen by the interceptor, and by the connection,
+ // which then returns an error.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
+
+ // A subsequent event will be seen by the interceptor and not by the
+ // connection.
+ callback->onVSyncEvent(456);
+ expectInterceptCallReceived(456);
+ EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
+
+ // EventThread should disable vsync callbacks with the second event
+ expectVSyncSetEnabledCallReceived(false);
+}
+
+TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
+ ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
+ sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
+ mThread->setVsyncRate(1, errorConnection);
+
+ // EventThread should enable vsync callbacks, and set a callback interface
+ // pointer to use them with the VSync source.
+ expectVSyncSetEnabledCallReceived(true);
+ auto callback = expectVSyncSetCallbackCallReceived();
+ ASSERT_TRUE(callback);
+
+ // The first event will be seen by the interceptor, and by the connection,
+ // which then returns an non-fatal error.
+ callback->onVSyncEvent(123);
+ expectInterceptCallReceived(123);
+ expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
+
+ // A subsequent event will be seen by the interceptor, and by the connection,
+ // which still then returns an non-fatal error.
+ callback->onVSyncEvent(456);
+ expectInterceptCallReceived(456);
+ expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
+
+ // EventThread will not disable vsync callbacks as the errors are non-fatal.
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
+TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
+ mThread->setPhaseOffset(321);
+ expectVSyncSetPhaseOffsetCallReceived(321);
+}
+
+TEST_F(EventThreadTest, postHotplugPrimaryDisconnect) {
+ mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, false);
+ expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, false);
+}
+
+TEST_F(EventThreadTest, postHotplugPrimaryConnect) {
+ mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true);
+ expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, true);
+}
+
+TEST_F(EventThreadTest, postHotplugExternalDisconnect) {
+ mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, false);
+ expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, false);
+}
+
+TEST_F(EventThreadTest, postHotplugExternalConnect) {
+ mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, true);
+ expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, true);
+}
+
+TEST_F(EventThreadTest, postHotplugVirtualDisconnectIsFilteredOut) {
+ mThread->onHotplugReceived(DisplayDevice::DISPLAY_VIRTUAL, false);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index a4e7361..f1556d8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -89,11 +89,19 @@
auto onInitializeDisplays() { return mFlinger->onInitializeDisplays(); }
+ auto setPowerModeInternal(const sp<DisplayDevice>& hw, int mode, bool stateLockHeld = false) {
+ return mFlinger->setPowerModeInternal(hw, mode, stateLockHeld);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
const auto& getAnimFrameTracker() const { return mFlinger->mAnimFrameTracker; }
+ const auto& getHasPoweredOff() const { return mFlinger->mHasPoweredOff; }
+ const auto& getHWVsyncAvailable() const { return mFlinger->mHWVsyncAvailable; }
+ const auto& getVisibleRegionsDirty() const { return mFlinger->mVisibleRegionsDirty; }
+
const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
/* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
index 93769a5..ac08293 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
@@ -61,6 +61,7 @@
MOCK_METHOD0(setupLayerBlackedOut, void());
MOCK_METHOD4(setupFillWithColor, void(float, float, float, float));
MOCK_METHOD1(setupColorTransform, void(const mat4&));
+ MOCK_METHOD1(setSaturationMatrix, void(const mat4&));
MOCK_METHOD0(disableTexturing, void());
MOCK_METHOD0(disableBlending, void());
MOCK_METHOD1(setSourceY410BT2020, void(bool));
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index c42e811..3db8a39 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1268,19 +1268,19 @@
//
// TODO(jessehall): The error path here is the same as DestroySwapchain,
// but not the non-error path. Should refactor/unify.
- if (!swapchain->shared) {
- for (uint32_t i = 0; i < num_images; i++) {
- Swapchain::Image& img = swapchain->images[i];
- if (img.dequeued) {
+ for (uint32_t i = 0; i < num_images; i++) {
+ Swapchain::Image& img = swapchain->images[i];
+ if (img.dequeued) {
+ if (!swapchain->shared) {
surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
img.dequeue_fence);
img.dequeue_fence = -1;
img.dequeued = false;
}
- if (result != VK_SUCCESS) {
- if (img.image)
- dispatch.DestroyImage(device, img.image, nullptr);
- }
+ }
+ if (result != VK_SUCCESS) {
+ if (img.image)
+ dispatch.DestroyImage(device, img.image, nullptr);
}
}