diff options
49 files changed, 2191 insertions, 508 deletions
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 70164eacf6..fe9dd568fc 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -58,6 +58,7 @@ using std::string; #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 @@ static const TracingCategory k_categories[] = { { 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 @@ static bool setTraceOverwriteEnable(bool enable) 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 @@ static bool setUpKernelTracing() { bool ok = true; + ok &= setUserInitiatedTraceProperty(true); + // Set up the tracing options. ok &= setCategoriesEnableFromFile(g_categoriesFile); ok &= setTraceOverwriteEnable(g_traceOverwrite); @@ -884,6 +900,7 @@ static void cleanUpKernelTracing() 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 a9c5c82270..d3d07115b9 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -83,6 +83,11 @@ on late-init 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 93a29bab73..f4e5b9814c 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 3e7d2a07bc..9d897f523d 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1504,6 +1504,7 @@ static void DumpstateRadioCommon() { // 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 @@ static void DumpstateTelephonyOnly() { 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 624d7d6d29..9615a75a07 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -309,7 +309,9 @@ static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vd // 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 3ae56dba65..e90cf3bab2 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -115,7 +115,7 @@ static int otapreopt_chroot(const int argc, char **arg) { 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 @@ static int otapreopt_chroot(const int argc, char **arg) { // 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 0000000000..013f278f34 --- /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 289433b0bd..449a9e9aa7 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -46,7 +46,7 @@ uint32_t BpBinder::sBinderProxyCountHighWatermark = 2500; 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 @@ void BpBinder::ObjectManager::kill() 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 @@ BpBinder::BpBinder(int32_t handle, int32_t trackedUid) 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 @@ BpBinder::~BpBinder() 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 @@ void BpBinder::onFirstRef() { 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 ba9bf61f8c..fd552b4f4d 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -409,6 +409,15 @@ void IPCThreadState::flushCommands() 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::processPendingDerefs() } } +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 @@ status_t IPCThreadState::transact(int32_t handle, 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 @@ void IPCThreadState::decStrongHandle(int32_t handle) 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 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) 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 245607e74e..c1d9a9a8f7 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -64,9 +64,9 @@ public: 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 @@ private: status_t getAndExecuteCommand(); status_t executeCommand(int32_t command); void processPendingDerefs(); + void processPostWriteDerefs(); void clearCaller(); @@ -118,7 +119,8 @@ private: 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 0183621299..fedc7893db 100644 --- a/opengl/libs/EGL/getProcAddress.cpp +++ b/opengl/libs/EGL/getProcAddress.cpp @@ -80,46 +80,44 @@ namespace android { #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 62e9ce0616..1025a88eaf 100644 --- a/services/sensorservice/RecentEventLogger.cpp +++ b/services/sensorservice/RecentEventLogger.cpp @@ -100,7 +100,8 @@ bool RecentEventLogger::populateLastEvent(sensors_event_t *event) const { 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 6a69844d9f..c2bb6ad6b4 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -22,6 +22,7 @@ cc_defaults { "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 6feec53257..17ed9aa00c 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -553,7 +553,7 @@ Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime default: break; } - setDataSpace(dataSpace); + mCurrentDataSpace = dataSpace; Rect crop(mConsumer->getCurrentCrop()); const uint32_t transform(mConsumer->getCurrentTransform()); @@ -660,10 +660,10 @@ void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) 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 @@ void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityT 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 c87b66981d..512564c2db 100644 --- a/services/surfaceflinger/ColorLayer.cpp +++ b/services/surfaceflinger/ColorLayer.cpp @@ -77,9 +77,9 @@ void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { 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 2b1e5775ba..9ecbacc86b 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 @@ namespace android { 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 @@ using android::ui::RenderIntent; 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 @@ DisplayDevice::DisplayDevice( 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 @@ DisplayDevice::DisplayDevice( 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 @@ DisplayDevice::DisplayDevice( } 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 @@ void DisplayDevice::setProjection(int orientation, 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 @@ void DisplayDevice::dump(String8& result) const { 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 d051e33579..b3859b6793 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -91,7 +91,7 @@ public: 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 @@ public: // 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 @@ public: // 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 @@ public: 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 @@ private: // 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 @@ private: 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 @@ public: 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 3947318262..61758b63f1 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 @@ Error Display::getActiveConfig( 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 3ac06ec192..29d7a47ad8 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 @@ public: [[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 @@ private: 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 96d691ce9c..f5f7a821f0 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -318,6 +318,28 @@ std::shared_ptr<const HWC2::Display::Config> 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 d7f3b08d48..f9689482cc 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -164,6 +164,7 @@ public: 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 9be4e7cd8e..cafae53400 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 bb9c0703ed..bc271c8ec5 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 @@ EventThread::~EventThread() = default; 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::setVsyncRate(uint32_t count, const sp<EventThread::Connection> 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 @@ Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked( 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 97f0a359e2..9c13ed2755 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 EventThread : public android::EventThread, private VSyncSource::Callback { 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 @@ class EventThread : public android::EventThread, private VSyncSource::Callback { 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 @@ class EventThread : public android::EventThread, private VSyncSource::Callback { }; 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 @@ public: 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 @@ private: 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 @@ private: // 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 9043234132..2595ec1a05 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -98,8 +98,9 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& n 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 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& n 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 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& n CompositorTiming compositorTiming; flinger->getCompositorTiming(&compositorTiming); mFrameEventHistory.initializeCompositorTiming(compositorTiming); - } void Layer::onFirstRef() {} @@ -921,10 +920,7 @@ void Layer::pushPendingState() { } 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 @@ bool Layer::setFlags(uint8_t flags, uint8_t mask) { 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 @@ bool Layer::setLayerStack(uint32_t layerStack) { 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 @@ LayerDebugInfo Layer::getLayerDebugInfo() const { 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::detachChildren() { 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 @@ __attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::mak 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 @@ LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet, 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 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) 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 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) 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 632efbe23e..ae80043a5d 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -186,7 +186,6 @@ public: 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 @@ public: // dependent. Region activeTransparentRegion; Region requestedTransparentRegion; - ui::Dataspace dataSpace; int32_t appId; int32_t type; @@ -287,8 +285,6 @@ public: 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 @@ public: 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 @@ public: // SurfaceFlinger to complete a transaction. void commitChildList(); int32_t getZ() const; + void pushPendingState(); protected: // constant @@ -671,7 +670,6 @@ protected: // 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 @@ protected: 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 47156c19d2..84945247a6 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 a9adb4113a..88d7711bb8 100644 --- a/services/surfaceflinger/LayerVector.h +++ b/services/surfaceflinger/LayerVector.h @@ -32,22 +32,27 @@ class Layer; */ 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 46ec8e68ba..1a8edf3e79 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 @@ float RenderArea::getCaptureFillValue(CaptureFill captureFill) { * 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 @@ status_t RenderArea::updateDimensions() { 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 @@ status_t RenderArea::updateDimensions() { 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 4694403c2d..96e4b5f48b 100644 --- a/services/surfaceflinger/RenderArea.h +++ b/services/surfaceflinger/RenderArea.h @@ -30,16 +30,13 @@ public: 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 c218e4da50..09414fd56d 100644 --- a/services/surfaceflinger/RenderEngine/Description.cpp +++ b/services/surfaceflinger/RenderEngine/Description.cpp @@ -51,6 +51,10 @@ void Description::setProjectionMatrix(const mat4& mtx) { mProjectionMatrix = mtx; } +void Description::setSaturationMatrix(const mat4& mtx) { + mSaturationMatrix = mtx; +} + void Description::setColorMatrix(const mat4& mtx) { mColorMatrix = mtx; } @@ -78,6 +82,11 @@ bool Description::hasColorMatrix() const { 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 6ebb34018d..06eaf35125 100644 --- a/services/surfaceflinger/RenderEngine/Description.h +++ b/services/surfaceflinger/RenderEngine/Description.h @@ -42,12 +42,14 @@ public: 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 @@ private: // 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 64095ddfb8..90404fae6b 100644 --- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp +++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp @@ -147,6 +147,7 @@ GLES20RenderEngine::GLES20RenderEngine(uint32_t featureFlags) 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 @@ void GLES20RenderEngine::setupColorTransform(const mat4& colorTransform) { mState.setColorMatrix(colorTransform); } +void GLES20RenderEngine::setSaturationMatrix(const mat4& saturationMatrix) { + mState.setSaturationMatrix(saturationMatrix); +} + void GLES20RenderEngine::disableTexturing() { mState.disableTexture(); } @@ -343,14 +348,17 @@ void GLES20RenderEngine::drawMesh(const Mesh& mesh) { 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 @@ void GLES20RenderEngine::drawMesh(const Mesh& mesh) { // 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 c9e402de8c..de5761b9b0 100644 --- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h +++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h @@ -81,6 +81,7 @@ protected: 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 @@ protected: 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 fd2c9688d9..95adaca73b 100644 --- a/services/surfaceflinger/RenderEngine/Program.cpp +++ b/services/surfaceflinger/RenderEngine/Program.cpp @@ -135,13 +135,22 @@ void Program::setUniforms(const Description& desc) { 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 abb02905b5..5b61db9fbd 100644 --- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp +++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp @@ -129,7 +129,8 @@ ProgramCache::Key ProgramCache::computeKey(const Description& description) { 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 @@ void ProgramCache::generateToneMappingProcess(Formatter& fs, const Key& needs) { 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 @@ void ProgramCache::generateToneMappingProcess(Formatter& fs, const Key& needs) { } 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 @@ void ProgramCache::generateOETF(Formatter& fs, const Key& needs) { 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 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { } 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 d559464eb5..a14acaa68d 100644 --- a/services/surfaceflinger/RenderEngine/RenderEngine.h +++ b/services/surfaceflinger/RenderEngine/RenderEngine.h @@ -113,6 +113,7 @@ public: 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 @@ public: 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 588d24c45f..97edd357ed 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 @@ bool useTrebleTestingOverride() { 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 @@ SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) { 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 @@ SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) { 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 @@ void SurfaceFlinger::init() { 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 @@ void SurfaceFlinger::readPersistentProperties() { 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 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, // 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 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { 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 @@ void SurfaceFlinger::handleMessageRefresh() { mHadClientComposition = mHadClientComposition || getBE().mHwc->hasClientComposition(displayDevice->getHwcDisplayId()); } - mVsyncModulator.setLastFrameUsedRenderEngine(mHadClientComposition); + mVsyncModulator.onRefreshed(mHadClientComposition); mLayersWithQueuedFrames.clear(); } @@ -1860,7 +1882,6 @@ void SurfaceFlinger::rebuildLayerStacks() { // 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 @@ Dataspace SurfaceFlinger::getBestDataspace( 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 @@ void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& displayDevice, 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; - } - } - - // 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; - } + // respect hdrDataSpace only when there is modern HDR support + const bool isHdr = hdrDataSpace != Dataspace::UNKNOWN && + displayDevice->hasModernHdrSupport(hdrDataSpace); + if (isHdr) { + bestDataSpace = hdrDataSpace; } - *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; - } - - // 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; - } + 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; } - // 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 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( 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 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( 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 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( dispSurface, std::move(renderSurface), displayWidth, displayHeight, hasWideColorGamut, hdrCapabilities, getHwComposer().getSupportedPerFrameMetadata(hwcId), - hdrAndRenderIntents, initialPowerMode); + hwcColorModes, initialPowerMode); if (maxFrameBufferAcquiredBuffers >= 3) { nativeWindowSurface->preallocateBuffers(); @@ -2402,10 +2332,13 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( 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 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev 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 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev 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 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev } 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; - } - } else { - if (applyColorMatrix && currentColorMatrix != &colorMatrix) { - getRenderEngine().setupColorTransform(colorMatrix); - currentColorMatrix = &colorMatrix; + if (layer->isLegacyDataSpace() && needsLegacyColorMatrix) { + if (!legacyColorMatrixApplied) { + getRenderEngine().setSaturationMatrix(mLegacySrgbSaturationMatrix); + legacyColorMatrixApplied = true; } + } else if (legacyColorMatrixApplied) { + getRenderEngine().setSaturationMatrix(mat4()); + legacyColorMatrixApplied = false; } layer->draw(renderArea, clip); @@ -3111,9 +3032,12 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev 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 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, 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 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState 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::dumpBufferingStats(String8& result) const { 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 @@ status_t SurfaceFlinger::onTransact( 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 @@ status_t SurfaceFlinger::onTransact( } // Is a DisplayColorSetting supported? case 1027: { - int32_t value = data.readInt32(); - switch (value) { - case 0: + 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); - return NO_ERROR; - case 1: + break; + case DisplayColorSetting::UNMANAGED: reply->writeBool(true); - return NO_ERROR; - case 2: - reply->writeBool(mBuiltinDisplaySupportsEnhance); - return NO_ERROR; - default: - return BAD_VALUE; + 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 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, 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 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, 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 @@ void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, 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 @@ void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, 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 d9cf9461ca..8566b0387c 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -341,6 +341,8 @@ public: bool authenticateSurfaceTextureLocked( const sp<IGraphicBufferProducer>& bufferProducer) const; + int getPrimaryDisplayOrientation() const { return mPrimaryDisplayOrientation; } + private: friend class Client; friend class DisplayEventConnection; @@ -367,7 +369,7 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 3e5800e2a2..d526313994 100644 --- a/services/surfaceflinger/VSyncModulator.h +++ b/services/surfaceflinger/VSyncModulator.h @@ -28,6 +28,12 @@ namespace android { * 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 @@ public: } 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 @@ public: 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 @@ private: // 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 @@ private: } } + 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 2a924ae1c7..8255b41596 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 @@ static status_t startGraphicsAllocatorService() { 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 @@ static status_t startHidlServices() { } 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 @@ int main(int, char**) { 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 bcabe0dffc..39761dd9ad 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -20,6 +20,8 @@ cc_test { 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 0000000000..2245ee1a8a --- /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 08da1a2c17..9b308bfcc8 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -45,12 +45,13 @@ using testing::Mock; 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_REFRESH_RATE = 16'666'666; 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 @@ struct HwcDisplayVariant { 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 @@ struct HwcDisplayVariant { // 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 @@ struct WideColorNotSupportedVariant { 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 HdrNotSupportedVariant { } }; +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 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { // 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 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { 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 @@ TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) { 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 @@ void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessin 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 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) { Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this); Case::WideColorSupport::setupComposerCallExpectations(this); Case::HdrSupport::setupComposerCallExpectations(this); + Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); // -------------------------------------------------------------------- // Invocation @@ -2276,5 +2353,446 @@ TEST_F(DisplayTransactionTest, onInitializeDisplaysSetsUpPrimaryDisplay) { 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 0000000000..b34645463f --- /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 0000000000..80fdb80264 --- /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 a4e73613cd..f1556d8bc7 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -89,11 +89,19 @@ public: 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 93769a53e4..ac082933a3 100644 --- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h +++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h @@ -61,6 +61,7 @@ public: 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 c42e81132c..3db8a3962e 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -1268,19 +1268,19 @@ VkResult CreateSwapchainKHR(VkDevice device, // // 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); } } |