diff options
124 files changed, 3377 insertions, 1534 deletions
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index a4b00f8e0..1429bc8b0 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -171,6 +171,7 @@ static const TracingCategory k_categories[] = { { OPT, "events/clk/clk_disable/enable" }, { OPT, "events/clk/clk_enable/enable" }, { OPT, "events/power/cpu_frequency_limits/enable" }, + { OPT, "events/power/suspend_resume/enable" }, } }, { "membus", "Memory Bus Utilization", 0, { { REQ, "events/memory_bus/enable" }, diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index f1426b6bd..6e460a0d2 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -49,6 +49,8 @@ on late-init chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency_limits/enable chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_frequency/enable chmod 0666 /sys/kernel/tracing/events/power/gpu_frequency/enable + chmod 0666 /sys/kernel/debug/tracing/events/power/suspend_resume/enable + chmod 0666 /sys/kernel/tracing/events/power/suspend_resume/enable chmod 0666 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable chmod 0666 /sys/kernel/tracing/events/cpufreq_interactive/enable chmod 0666 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 4ac7b689c..5de40776b 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1594,6 +1594,8 @@ static Dumpstate::RunStatus DumpstateDefault() { static void DumpstateRadioCommon() { DumpIpTablesAsRoot(); + ds.AddDir(LOGPERSIST_DATA_DIR, false); + if (!DropRootUser()) { return; } @@ -1604,6 +1606,7 @@ static void DumpstateRadioCommon() { DoKmsg(); DumpIpAddrAndRules(); dump_route_tables(); + DumpHals(); RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"}, CommandOptions::WithTimeout(10).Build()); @@ -1673,8 +1676,6 @@ static void DumpstateWifiOnly() { RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); - DumpHals(); - printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); printf("========================================================\n"); diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index caac2e89a..34727270e 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -2136,26 +2136,6 @@ binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath, return *_aidl_return ? ok() : error("viewcompiler failed"); } -binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) { - ENFORCE_UID(AID_SYSTEM); - std::lock_guard<std::recursive_mutex> lock(mLock); - - const char* instruction_set = instructionSet.c_str(); - - char boot_marker_path[PKG_PATH_MAX]; - sprintf(boot_marker_path, - "%s/%s/%s/.booting", - android_data_dir.c_str(), - DALVIK_CACHE, - instruction_set); - - ALOGV("mark_boot_complete : %s", boot_marker_path); - if (unlink(boot_marker_path) != 0) { - return error(StringPrintf("Failed to unlink %s", boot_marker_path)); - } - return ok(); -} - binder::Status InstalldNativeService::linkNativeLibraryDirectory( const std::unique_ptr<std::string>& uuid, const std::string& packageName, const std::string& nativeLibPath32, int32_t userId) { diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 2b7bf33cb..eba966d4a 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -123,7 +123,6 @@ public: int32_t uid); binder::Status removeIdmap(const std::string& overlayApkPath); binder::Status rmPackageDir(const std::string& packageDir); - binder::Status markBootComplete(const std::string& instructionSet); binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags); binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid, diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 26e9984f1..b795c0264 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -75,7 +75,6 @@ interface IInstalld { void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid); void removeIdmap(@utf8InCpp String overlayApkPath); void rmPackageDir(@utf8InCpp String packageDir); - void markBootComplete(@utf8InCpp String instructionSet); void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes, long cacheReservedBytes, int flags); void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid, diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index dbb4f2237..5fb13a5c5 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -336,6 +336,10 @@ class RunDex2Oat : public ExecVHelper { ? "dalvik.vm.dex2oat-threads" : "dalvik.vm.boot-dex2oat-threads"; std::string dex2oat_threads_arg = MapPropertyToArg(threads_property, "-j%s"); + const char* cpu_set_property = post_bootcomplete + ? "dalvik.vm.dex2oat-cpu-set" + : "dalvik.vm.boot-dex2oat-cpu-set"; + std::string dex2oat_cpu_set_arg = MapPropertyToArg(cpu_set_property, "--cpu-set=%s"); std::string bootclasspath; char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH"); @@ -507,6 +511,7 @@ class RunDex2Oat : public ExecVHelper { AddArg(image_block_size_arg); AddArg(dex2oat_compiler_filter_arg); AddArg(dex2oat_threads_arg); + AddArg(dex2oat_cpu_set_arg); AddArg(dex2oat_swap_fd); AddArg(dex2oat_image_fd); diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index de7b2499b..9bc5fc81d 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -478,6 +478,10 @@ private: "-j", false, cmd); + AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-cpu-set", + "--cpu-set=", + false, + cmd); AddCompilerOptionFromSystemProperty( StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(), "--instruction-set-variant=", diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index ad7791e07..50f117dd1 100644 --- a/data/etc/car_core_hardware.xml +++ b/data/etc/car_core_hardware.xml @@ -40,7 +40,6 @@ <feature name="android.software.voice_recognizers" notLowRam="true" /> <feature name="android.software.backup" /> <feature name="android.software.home_screen" /> - <feature name="android.software.print" /> <feature name="android.software.companion_device_setup" /> <feature name="android.software.autofill" /> <feature name="android.software.cant_save_state" /> diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h index 4365a3c4e..d23e3b776 100644 --- a/include/input/IInputFlinger.h +++ b/include/input/IInputFlinger.h @@ -37,7 +37,6 @@ public: virtual void setInputWindows(const std::vector<InputWindowInfo>& inputHandles, const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0; - virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0; virtual void registerInputChannel(const sp<InputChannel>& channel) = 0; virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0; }; @@ -51,8 +50,7 @@ public: enum { SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, REGISTER_INPUT_CHANNEL_TRANSACTION, - UNREGISTER_INPUT_CHANNEL_TRANSACTION, - TRANSFER_TOUCH_FOCUS + UNREGISTER_INPUT_CHANNEL_TRANSACTION }; virtual status_t onTransact(uint32_t code, const Parcel& data, diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 40f6b4380..047780171 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -16,7 +16,9 @@ #include <set> #include <android-base/file.h> +#include <android-base/properties.h> #include <android-base/stringprintf.h> +#include <android-base/strings.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <dumputils/dump_utils.h> #include <log/log.h> @@ -46,6 +48,8 @@ static const char* native_processes_to_dump[] = { static const char* hal_interfaces_to_dump[] { "android.hardware.audio@2.0::IDevicesFactory", "android.hardware.audio@4.0::IDevicesFactory", + "android.hardware.audio@5.0::IDevicesFactory", + "android.hardware.biometrics.face@1.0::IBiometricsFace", "android.hardware.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.drm@1.0::IDrmFactory", @@ -60,16 +64,40 @@ static const char* hal_interfaces_to_dump[] { "android.hardware.sensors@1.0::ISensors", "android.hardware.thermal@2.0::IThermal", "android.hardware.vr@1.0::IVr", + "android.hardware.automotive.audiocontrol@1.0::IAudioControl", + "android.hardware.automotive.vehicle@2.0::IVehicle", + "android.hardware.automotive.evs@1.0::IEvsCamera", NULL, }; -bool should_dump_hal_interface(const char* interface) { +/* list of extra hal interfaces to dump containing process during native dumps */ +// This is filled when dumpstate is called. +static std::set<const std::string> extra_hal_interfaces_to_dump; + +static void read_extra_hals_to_dump_from_property() { + // extra hals to dump are already filled + if (extra_hal_interfaces_to_dump.size() > 0) { + return; + } + std::string value = android::base::GetProperty("ro.dump.hals.extra", ""); + std::vector<std::string> tokens = android::base::Split(value, ","); + for (const auto &token : tokens) { + std::string trimmed_token = android::base::Trim(token); + if (trimmed_token.length() == 0) { + continue; + } + extra_hal_interfaces_to_dump.insert(trimmed_token); + } +} + +// check if interface is included in either default hal list or extra hal list +bool should_dump_hal_interface(const std::string& interface) { for (const char** i = hal_interfaces_to_dump; *i; i++) { - if (!strcmp(*i, interface)) { + if (interface == *i) { return true; } } - return false; + return extra_hal_interfaces_to_dump.find(interface) != extra_hal_interfaces_to_dump.end(); } bool should_dump_native_traces(const char* path) { @@ -89,13 +117,15 @@ std::set<int> get_interesting_hal_pids() { sp<IServiceManager> manager = IServiceManager::getService(); std::set<int> pids; + read_extra_hals_to_dump_from_property(); + Return<void> ret = manager->debugDump([&](auto& hals) { for (const auto &info : hals) { if (info.pid == static_cast<int>(IServiceManager::PidConstant::NO_PID)) { continue; } - if (!should_dump_hal_interface(info.interfaceName.c_str())) { + if (!should_dump_hal_interface(info.interfaceName)) { continue; } diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index bb9e263ac..6e59cc504 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -139,12 +139,8 @@ static const std::string getSystemNativeLibraries(NativeLibrary type) { return env; } -int GraphicsEnv::getCanLoadSystemLibraries() { - if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { - // Return an integer value since this crosses library boundaries - return 1; - } - return 0; +bool GraphicsEnv::isDebuggable() { + return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0; } void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path, diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 937bcd9ac..227b4587c 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -84,7 +84,16 @@ private: public: static GraphicsEnv& getInstance(); - int getCanLoadSystemLibraries(); + // Check if the process is debuggable. It returns false except in any of the + // following circumstances: + // 1. ro.debuggable=1 (global debuggable enabled). + // 2. android:debuggable="true" in the manifest for an individual app. + // 3. An app which explicitly calls prctl(PR_SET_DUMPABLE, 1). + // 4. GraphicsEnv calls prctl(PR_SET_DUMPABLE, 1) in the presence of + // <meta-data android:name="com.android.graphics.injectLayers.enable" + // android:value="true"/> + // in the application manifest. + bool isDebuggable(); // Set a search path for loading graphics drivers. The path is a list of // directories separated by ':'. A directory can be contained in a zip file diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 34575f5d4..e3e63ee54 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -34,6 +34,7 @@ cc_library_shared { "BufferItemConsumer.cpp", "ConsumerBase.cpp", "CpuConsumer.cpp", + "DebugEGLImageTracker.cpp", "DisplayEventReceiver.cpp", "GLConsumer.cpp", "GuiConfig.cpp", diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index 528bfb194..3a7cb4445 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -166,7 +166,9 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, mCore->mFreeBuffers.push_back(front->mSlot); } - listener = mCore->mConnectedProducerListener; + if (mCore->mBufferReleasedCbEnabled) { + listener = mCore->mConnectedProducerListener; + } ++numDroppedBuffers; } @@ -457,7 +459,9 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, mCore->mFreeBuffers.push_back(slot); } - listener = mCore->mConnectedProducerListener; + if (mCore->mBufferReleasedCbEnabled) { + listener = mCore->mConnectedProducerListener; + } BQ_LOGV("releaseBuffer: releasing slot %d", slot); mCore->mDequeueCondition.notify_all(); @@ -668,7 +672,7 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers); mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers; VALIDATE_CONSISTENCY(); - if (delta < 0) { + if (delta < 0 && mCore->mBufferReleasedCbEnabled) { listener = mCore->mConsumerListener; } } diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index e0e3431ca..0264bd24a 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -65,6 +65,7 @@ BufferQueueCore::BufferQueueCore() : mConnectedApi(NO_CONNECTED_API), mLinkedToDeath(), mConnectedProducerListener(), + mBufferReleasedCbEnabled(false), mSlots(), mQueue(), mFreeSlots(), @@ -260,6 +261,12 @@ void BufferQueueCore::freeAllBuffersLocked() { } void BufferQueueCore::discardFreeBuffersLocked() { + // Notify producer about the discarded buffers. + if (mConnectedProducerListener != nullptr && mFreeBuffers.size() > 0) { + std::vector<int32_t> freeBuffers(mFreeBuffers.begin(), mFreeBuffers.end()); + mConnectedProducerListener->onBuffersDiscarded(freeBuffers); + } + for (int s : mFreeBuffers) { mFreeSlots.insert(s); clearBufferSlotLocked(s); diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 9c311a314..a317aaf2f 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -936,6 +936,15 @@ status_t BufferQueueProducer::queueBuffer(int slot, } } + // Make sure to merge the damage rect from the frame we're about + // to drop into the new frame's damage rect. + if (last.mSurfaceDamage.bounds() == Rect::INVALID_RECT || + item.mSurfaceDamage.bounds() == Rect::INVALID_RECT) { + item.mSurfaceDamage = Region::INVALID_REGION; + } else { + item.mSurfaceDamage |= last.mSurfaceDamage; + } + // Overwrite the droppable buffer with the incoming one mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item; frameReplacedListener = mCore->mConsumerListener; @@ -1212,9 +1221,8 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, } mCore->mLinkedToDeath = listener; } - if (listener->needsReleaseNotify()) { - mCore->mConnectedProducerListener = listener; - } + mCore->mConnectedProducerListener = listener; + mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify(); } break; default: diff --git a/libs/gui/DebugEGLImageTracker.cpp b/libs/gui/DebugEGLImageTracker.cpp new file mode 100644 index 000000000..ab6f36444 --- /dev/null +++ b/libs/gui/DebugEGLImageTracker.cpp @@ -0,0 +1,98 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/stringprintf.h> +#include <cutils/properties.h> +#include <gui/DebugEGLImageTracker.h> + +#include <cinttypes> +#include <unordered_map> + +using android::base::StringAppendF; + +std::mutex DebugEGLImageTracker::mInstanceLock; +std::atomic<DebugEGLImageTracker *> DebugEGLImageTracker::mInstance; + +class DebugEGLImageTrackerNoOp : public DebugEGLImageTracker { +public: + DebugEGLImageTrackerNoOp() = default; + ~DebugEGLImageTrackerNoOp() override = default; + void create(const char * /*from*/) override {} + void destroy(const char * /*from*/) override {} + + void dump(std::string & /*result*/) override {} +}; + +class DebugEGLImageTrackerImpl : public DebugEGLImageTracker { +public: + DebugEGLImageTrackerImpl() = default; + ~DebugEGLImageTrackerImpl() override = default; + void create(const char * /*from*/) override; + void destroy(const char * /*from*/) override; + + void dump(std::string & /*result*/) override; + +private: + std::mutex mLock; + std::unordered_map<std::string, int64_t> mCreateTracker; + std::unordered_map<std::string, int64_t> mDestroyTracker; + + int64_t mTotalCreated = 0; + int64_t mTotalDestroyed = 0; +}; + +DebugEGLImageTracker *DebugEGLImageTracker::getInstance() { + std::lock_guard lock(mInstanceLock); + if (mInstance == nullptr) { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.enable_egl_image_tracker", value, "0"); + const bool enabled = static_cast<bool>(atoi(value)); + + if (enabled) { + mInstance = new DebugEGLImageTrackerImpl(); + } else { + mInstance = new DebugEGLImageTrackerNoOp(); + } + } + + return mInstance; +} + +void DebugEGLImageTrackerImpl::create(const char *from) { + std::lock_guard lock(mLock); + mCreateTracker[from]++; + mTotalCreated++; +} + +void DebugEGLImageTrackerImpl::destroy(const char *from) { + std::lock_guard lock(mLock); + mDestroyTracker[from]++; + mTotalDestroyed++; +} + +void DebugEGLImageTrackerImpl::dump(std::string &result) { + std::lock_guard lock(mLock); + StringAppendF(&result, "Live EGL Image objects: %" PRIi64 "\n", + mTotalCreated - mTotalDestroyed); + StringAppendF(&result, "Total EGL Image created: %" PRIi64 "\n", mTotalCreated); + for (const auto &[from, count] : mCreateTracker) { + StringAppendF(&result, "\t%s: %" PRIi64 "\n", from.c_str(), count); + } + StringAppendF(&result, "Total EGL Image destroyed: %" PRIi64 "\n", mTotalDestroyed); + for (const auto &[from, count] : mDestroyTracker) { + StringAppendF(&result, "\t%s: %" PRIi64 "\n", from.c_str(), count); + } +} diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index f5cf1c4d5..b8faa2df4 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -32,10 +32,11 @@ namespace android { // --------------------------------------------------------------------------- -DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) { +DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource, + ISurfaceComposer::ConfigChanged configChanged) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); if (sf != nullptr) { - mEventConnection = sf->createDisplayEventConnection(vsyncSource); + mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged); if (mEventConnection != nullptr) { mDataChannel = std::make_unique<gui::BitTube>(); mEventConnection->stealReceiveChannel(mDataChannel.get()); diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 8d66154bd..8199c9858 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -34,6 +34,7 @@ #include <math/mat4.h> #include <gui/BufferItem.h> +#include <gui/DebugEGLImageTracker.h> #include <gui/GLConsumer.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> @@ -944,6 +945,7 @@ GLConsumer::EglImage::~EglImage() { if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("~EglImage: eglDestroyImageKHR failed"); } + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); eglTerminate(mEglDisplay); } } @@ -957,6 +959,7 @@ status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("createIfNeeded: eglDestroyImageKHR failed"); } + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); eglTerminate(mEglDisplay); mEglImage = EGL_NO_IMAGE_KHR; mEglDisplay = EGL_NO_DISPLAY; @@ -1006,7 +1009,10 @@ EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy, EGLint error = eglGetError(); ALOGE("error creating EGLImage: %#x", error); eglTerminate(dpy); + } else { + DEBUG_EGL_IMAGE_TRACKER_CREATE(); } + return image; } diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp index 936063a5b..808e3369f 100644 --- a/libs/gui/IProducerListener.cpp +++ b/libs/gui/IProducerListener.cpp @@ -24,6 +24,7 @@ namespace android { enum { ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION, NEEDS_RELEASE_NOTIFY, + ON_BUFFERS_DISCARDED, }; class BpProducerListener : public BpInterface<IProducerListener> @@ -56,6 +57,13 @@ public: } return result; } + + virtual void onBuffersDiscarded(const std::vector<int>& discardedSlots) { + Parcel data, reply; + data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor()); + data.writeInt32Vector(discardedSlots); + remote()->transact(ON_BUFFERS_DISCARDED, data, &reply, IBinder::FLAG_ONEWAY); + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -76,6 +84,10 @@ public: virtual bool needsReleaseNotify() override { return mBase->needsReleaseNotify(); } + + virtual void onBuffersDiscarded(const std::vector<int32_t>& discardedSlots) override { + return mBase->onBuffersDiscarded(discardedSlots); + } }; IMPLEMENT_HYBRID_META_INTERFACE(ProducerListener, @@ -92,6 +104,17 @@ status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data, CHECK_INTERFACE(IProducerListener, data, reply); reply->writeBool(needsReleaseNotify()); return NO_ERROR; + case ON_BUFFERS_DISCARDED: { + CHECK_INTERFACE(IProducerListener, data, reply); + std::vector<int32_t> discardedSlots; + status_t result = data.readInt32Vector(&discardedSlots); + if (result != NO_ERROR) { + ALOGE("ON_BUFFERS_DISCARDED failed to read discardedSlots: %d", result); + return result; + } + onBuffersDiscarded(discardedSlots); + return NO_ERROR; + } } return BBinder::onTransact(code, data, reply, flags); } @@ -104,4 +127,7 @@ bool BnProducerListener::needsReleaseNotify() { return true; } +void BnProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*discardedSlots*/) { +} + } // namespace android diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 6c9d81ab5..e487792c8 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -278,8 +278,8 @@ public: return NO_ERROR; } - virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource) - { + virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource, + ConfigChanged configChanged) { Parcel data, reply; sp<IDisplayEventConnection> result; int err = data.writeInterfaceToken( @@ -288,6 +288,7 @@ public: return result; } data.writeInt32(static_cast<int32_t>(vsyncSource)); + data.writeInt32(static_cast<int32_t>(configChanged)); err = remote()->transact( BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION, data, &reply); @@ -1155,8 +1156,11 @@ status_t BnSurfaceComposer::onTransact( } case CREATE_DISPLAY_EVENT_CONNECTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IDisplayEventConnection> connection(createDisplayEventConnection( - static_cast<ISurfaceComposer::VsyncSource>(data.readInt32()))); + auto vsyncSource = static_cast<ISurfaceComposer::VsyncSource>(data.readInt32()); + auto configChanged = static_cast<ISurfaceComposer::ConfigChanged>(data.readInt32()); + + sp<IDisplayEventConnection> connection( + createDisplayEventConnection(vsyncSource, configChanged)); reply->writeStrongBinder(IInterface::asBinder(connection)); return NO_ERROR; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index e6eb327c6..b822319d2 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -35,6 +35,7 @@ #include <ui/DisplayStatInfo.h> #include <ui/Fence.h> +#include <ui/GraphicBuffer.h> #include <ui/HdrCapabilities.h> #include <ui/Region.h> @@ -1287,6 +1288,14 @@ int Surface::connect(int api, const sp<IProducerListener>& listener) { } int Surface::connect( + int api, bool reportBufferRemoval, const sp<SurfaceListener>& sListener) { + if (sListener != nullptr) { + mListenerProxy = new ProducerListenerProxy(this, sListener); + } + return connect(api, mListenerProxy, reportBufferRemoval); +} + +int Surface::connect( int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) { ATRACE_CALL(); ALOGV("Surface::connect"); @@ -1684,6 +1693,28 @@ void Surface::freeAllBuffers() { } } +status_t Surface::getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots, + std::vector<sp<GraphicBuffer>>* outBuffers) { + ALOGV("Surface::getAndFlushBuffersFromSlots"); + for (int32_t i : slots) { + if (i < 0 || i >= NUM_BUFFER_SLOTS) { + ALOGE("%s: Invalid slotIndex: %d", __FUNCTION__, i); + return BAD_VALUE; + } + } + + Mutex::Autolock lock(mMutex); + for (int32_t i : slots) { + if (mSlots[i].buffer == nullptr) { + ALOGW("%s: Discarded slot %d doesn't contain buffer!", __FUNCTION__, i); + continue; + } + outBuffers->push_back(mSlots[i].buffer); + mSlots[i].buffer = nullptr; + } + return OK; +} + void Surface::setSurfaceDamage(android_native_rect_t* rects, size_t numRects) { ATRACE_CALL(); ALOGV("Surface::setSurfaceDamage"); @@ -1920,7 +1951,8 @@ status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) return OK; } -status_t Surface::attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffer) { +status_t Surface::attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer, + Dataspace dataspace) { if (buffer == nullptr) { return BAD_VALUE; } @@ -1929,6 +1961,11 @@ status_t Surface::attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffe if (err != OK) { return err; } + ui::Dataspace tmpDataspace = surface->getBuffersDataSpace(); + err = surface->setBuffersDataSpace(dataspace); + if (err != OK) { + return err; + } err = surface->attachBuffer(buffer->getNativeBuffer()); if (err != OK) { return err; @@ -1937,8 +1974,30 @@ status_t Surface::attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffe if (err != OK) { return err; } + err = surface->setBuffersDataSpace(tmpDataspace); + if (err != OK) { + return err; + } err = surface->disconnect(NATIVE_WINDOW_API_CPU); return err; } +void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_t>& slots) { + ATRACE_CALL(); + sp<Surface> parent = mParent.promote(); + if (parent == nullptr) { + return; + } + + std::vector<sp<GraphicBuffer>> discardedBufs; + status_t res = parent->getAndFlushBuffersFromSlots(slots, &discardedBufs); + if (res != OK) { + ALOGE("%s: Failed to get buffers from slots: %s(%d)", __FUNCTION__, + strerror(-res), res); + return; + } + + mSurfaceListener->onBuffersDiscarded(discardedBufs); +} + }; // namespace android diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h index 690a85f39..17617bce1 100644 --- a/libs/gui/include/gui/BufferQueueCore.h +++ b/libs/gui/include/gui/BufferQueueCore.h @@ -189,8 +189,12 @@ private: sp<IProducerListener> mLinkedToDeath; // mConnectedProducerListener is used to handle the onBufferReleased - // notification. + // and onBuffersDiscarded notification. sp<IProducerListener> mConnectedProducerListener; + // mBufferReleasedCbEnabled is used to indicate whether onBufferReleased() + // callback is registered by the listener. When set to false, + // mConnectedProducerListener will not trigger onBufferReleased() callback. + bool mBufferReleasedCbEnabled; // mSlots is an array of buffer slots that must be mirrored on the producer // side. This allows buffer ownership to be transferred between the producer diff --git a/libs/gui/include/gui/DebugEGLImageTracker.h b/libs/gui/include/gui/DebugEGLImageTracker.h new file mode 100644 index 000000000..5d369c9a3 --- /dev/null +++ b/libs/gui/include/gui/DebugEGLImageTracker.h @@ -0,0 +1,44 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <atomic> +#include <mutex> +#include <string> + +class DebugEGLImageTracker { +public: + static DebugEGLImageTracker *getInstance(); + + virtual void create(const char *from) = 0; + virtual void destroy(const char *from) = 0; + + virtual void dump(std::string &result) = 0; + +protected: + DebugEGLImageTracker() = default; + virtual ~DebugEGLImageTracker() = default; + DebugEGLImageTracker(const DebugEGLImageTracker &) = delete; + + static std::mutex mInstanceLock; + static std::atomic<DebugEGLImageTracker *> mInstance; +}; + +#define DEBUG_EGL_IMAGE_TRACKER_CREATE() \ + (DebugEGLImageTracker::getInstance()->create(__PRETTY_FUNCTION__)) +#define DEBUG_EGL_IMAGE_TRACKER_DESTROY() \ + (DebugEGLImageTracker::getInstance()->destroy(__PRETTY_FUNCTION__))
\ No newline at end of file diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 22de75149..a558cf9e1 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -88,10 +88,13 @@ public: * DisplayEventReceiver creates and registers an event connection with * SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate * or requestNextVsync to receive them. + * To receive Config Changed events specify this in the constructor. * Other events start being delivered immediately. */ explicit DisplayEventReceiver( - ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp); + ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp, + ISurfaceComposer::ConfigChanged configChanged = + ISurfaceComposer::eConfigChangedSuppress); /* * ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h index a13d8e494..32a3690ff 100644 --- a/libs/gui/include/gui/IProducerListener.h +++ b/libs/gui/include/gui/IProducerListener.h @@ -17,6 +17,8 @@ #ifndef ANDROID_GUI_IPRODUCERLISTENER_H #define ANDROID_GUI_IPRODUCERLISTENER_H +#include <vector> + #include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h> #include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h> #include <binder/IInterface.h> @@ -44,6 +46,9 @@ public: // multiple threads. virtual void onBufferReleased() = 0; // Asynchronous virtual bool needsReleaseNotify() = 0; + // onBuffersFreed is called from IGraphicBufferConsumer::discardFreeBuffers + // to notify the producer that certain free buffers are discarded by the consumer. + virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) = 0; // Asynchronous }; class IProducerListener : public ProducerListener, public IInterface @@ -65,6 +70,7 @@ public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); virtual bool needsReleaseNotify(); + virtual void onBuffersDiscarded(const std::vector<int32_t>& slots); }; class DummyProducerListener : public BnProducerListener diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index e2f77365b..c84910b6e 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -90,6 +90,8 @@ public: eVsyncSourceSurfaceFlinger = 1 }; + enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 }; + /* * Create a connection with SurfaceFlinger. */ @@ -97,7 +99,8 @@ public: /* return an IDisplayEventConnection */ virtual sp<IDisplayEventConnection> createDisplayEventConnection( - VsyncSource vsyncSource = eVsyncSourceApp) = 0; + VsyncSource vsyncSource = eVsyncSourceApp, + ConfigChanged configChanged = eConfigChangedSuppress) = 0; /* create a virtual display * requires ACCESS_SURFACE_FLINGER permission. diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 0c471bb70..a5641b07e 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -20,6 +20,7 @@ #include <gui/BufferQueueDefs.h> #include <gui/HdrMetadata.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/IProducerListener.h> #include <ui/ANativeObjectBase.h> #include <ui/GraphicTypes.h> @@ -35,6 +36,21 @@ namespace android { class ISurfaceComposer; +/* This is the same as ProducerListener except that onBuffersDiscarded is + * called with a vector of graphic buffers instead of buffer slots. + */ +class SurfaceListener : public virtual RefBase +{ +public: + SurfaceListener() = default; + virtual ~SurfaceListener() = default; + + virtual void onBufferReleased() = 0; + virtual bool needsReleaseNotify() = 0; + + virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) = 0; +}; + /* * An implementation of ANativeWindow that feeds graphics buffers into a * BufferQueue. @@ -283,6 +299,10 @@ public: sp<Fence>* outFence); virtual int attachBuffer(ANativeWindowBuffer*); + virtual int connect( + int api, bool reportBufferRemoval, + const sp<SurfaceListener>& sListener); + // When client connects to Surface with reportBufferRemoval set to true, any buffers removed // from this Surface will be collected and returned here. Once this method returns, these // buffers will no longer be referenced by this Surface unless they are attached to this @@ -292,12 +312,33 @@ public: ui::Dataspace getBuffersDataSpace(); - static status_t attachAndQueueBuffer(Surface* surface, sp<GraphicBuffer> buffer); + static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer, + ui::Dataspace dataspace); protected: enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; + class ProducerListenerProxy : public BnProducerListener { + public: + ProducerListenerProxy(wp<Surface> parent, sp<SurfaceListener> listener) + : mParent(parent), mSurfaceListener(listener) {} + virtual ~ProducerListenerProxy() {} + + virtual void onBufferReleased() { + mSurfaceListener->onBufferReleased(); + } + + virtual bool needsReleaseNotify() { + return mSurfaceListener->needsReleaseNotify(); + } + + virtual void onBuffersDiscarded(const std::vector<int32_t>& slots); + private: + wp<Surface> mParent; + sp<SurfaceListener> mSurfaceListener; + }; + void querySupportedTimestampsLocked() const; void freeAllBuffers(); @@ -465,6 +506,10 @@ protected: bool mReportRemovedBuffers = false; std::vector<sp<GraphicBuffer>> mRemovedBuffers; + + sp<IProducerListener> mListenerProxy; + status_t getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots, + std::vector<sp<GraphicBuffer>>* outBuffers); }; } // namespace android diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 119e888ed..406f21faf 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -998,12 +998,31 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { ASSERT_EQ(true, thirdSegment.usedThirdBuffer); } +struct BufferDiscardedListener : public BnProducerListener { +public: + BufferDiscardedListener() = default; + virtual ~BufferDiscardedListener() = default; + + virtual void onBufferReleased() {} + virtual bool needsReleaseNotify() { return false; } + virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) { + mDiscardedSlots.insert(mDiscardedSlots.end(), slots.begin(), slots.end()); + } + + const std::vector<int32_t>& getDiscardedSlots() const { return mDiscardedSlots; } +private: + // No need to use lock given the test triggers the listener in the same + // thread context. + std::vector<int32_t> mDiscardedSlots; +}; + TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { createBufferQueue(); sp<DummyConsumer> dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, + sp<BufferDiscardedListener> pl(new BufferDiscardedListener); + ASSERT_EQ(OK, mProducer->connect(pl, NATIVE_WINDOW_API_CPU, false, &output)); int slot = BufferQueue::INVALID_BUFFER_SLOT; @@ -1044,12 +1063,19 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + int releasedSlot = item.mSlot; + // Acquire 1 buffer, leaving 1 filled buffer in queue ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); // Now discard the free buffers ASSERT_EQ(OK, mConsumer->discardFreeBuffers()); + // Check onBuffersDiscarded is called with correct slots + auto buffersDiscarded = pl->getDiscardedSlots(); + ASSERT_EQ(buffersDiscarded.size(), 1); + ASSERT_EQ(buffersDiscarded[0], releasedSlot); + // Check no free buffers in dump String8 dumpString; mConsumer->dumpState(String8{}, &dumpString); diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index ff1ba0ad1..66a8a04c8 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -133,27 +133,6 @@ public: EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction()); } - void expectMotionEvent(int motionEventType, int x, int y) { - InputEvent *ev = consumeEvent(); - ASSERT_NE(ev, nullptr); - ASSERT_EQ(ev->getType(), AINPUT_EVENT_TYPE_MOTION); - MotionEvent *mev = static_cast<MotionEvent *>(ev); - EXPECT_EQ(motionEventType, mev->getAction()); - EXPECT_EQ(x, mev->getX(0)); - EXPECT_EQ(y, mev->getY(0)); - } - - void expectNoMotionEvent(int motionEventType) { - InputEvent *ev = consumeEvent(); - if (ev == nullptr || ev->getType() != AINPUT_EVENT_TYPE_MOTION) { - // Didn't find an event or a motion event so assume action didn't occur. - return; - } - - MotionEvent *mev = static_cast<MotionEvent *>(ev); - EXPECT_NE(motionEventType, mev->getAction()); - } - ~InputSurface() { mInputFlinger->unregisterInputChannel(mServerChannel); } @@ -278,15 +257,6 @@ void injectTap(int x, int y) { } } -void injectMotionEvent(std::string event, int x, int y) { - char *buf1, *buf2; - asprintf(&buf1, "%d", x); - asprintf(&buf2, "%d", y); - if (fork() == 0) { - execlp("input", "input", "motionevent", event.c_str(), buf1, buf2, NULL); - } -} - TEST_F(InputSurfacesTest, can_receive_input) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(100, 100); @@ -422,9 +392,11 @@ TEST_F(InputSurfacesTest, input_ignores_transparent_region) { surface->expectTap(1, 1); } -// Ensure we send the input to the right surface when the surface visibility changes due to the -// first buffer being submitted. ref: b/120839715 -TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) { +// TODO(b/139494112) update tests once we define expected behavior +// Ensure we still send input to the surface regardless of surface visibility changes due to the +// first buffer being submitted or alpha changes. +// Original bug ref: b/120839715 +TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> bufferSurface = InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); @@ -433,14 +405,14 @@ TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) { bufferSurface->showAt(10, 10); injectTap(11, 11); - bgSurface->expectTap(1, 1); + bufferSurface->expectTap(1, 1); postBuffer(bufferSurface->mSurfaceControl); injectTap(11, 11); bufferSurface->expectTap(1, 1); } -TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) { +TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> bufferSurface = InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); @@ -455,10 +427,10 @@ TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) { bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); }); injectTap(11, 11); - bgSurface->expectTap(1, 1); + bufferSurface->expectTap(1, 1); } -TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) { +TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); @@ -471,7 +443,7 @@ TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) { fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); }); injectTap(11, 11); - bgSurface->expectTap(1, 1); + fgSurface->expectTap(1, 1); } TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { @@ -491,26 +463,6 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { bgSurface->expectTap(1, 1); } -TEST_F(InputSurfacesTest, transfer_touch_focus) { - std::unique_ptr<InputSurface> fromSurface = makeSurface(100, 100); - - fromSurface->showAt(10, 10); - injectMotionEvent("DOWN", 11, 11); - fromSurface->expectMotionEvent(AMOTION_EVENT_ACTION_DOWN, 1, 1); - - std::unique_ptr<InputSurface> toSurface = makeSurface(100, 100); - toSurface->showAt(10, 10); - - sp<IBinder> fromToken = fromSurface->mServerChannel->getToken(); - sp<IBinder> toToken = toSurface->mServerChannel->getToken(); - SurfaceComposerClient::Transaction t; - t.transferTouchFocus(fromToken, toToken).apply(true); - - injectMotionEvent("UP", 11, 11); - toSurface->expectMotionEvent(AMOTION_EVENT_ACTION_UP, 1, 1); - fromSurface->expectNoMotionEvent(AMOTION_EVENT_ACTION_UP); -} - TEST_F(InputSurfacesTest, input_respects_outscreen) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(-1, -1); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 960cf1846..a8516872f 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -57,6 +57,37 @@ class FakeProducerFrameEventHistory; static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max(); +class DummySurfaceListener : public SurfaceListener { +public: + DummySurfaceListener(bool enableReleasedCb = false) : + mEnableReleaseCb(enableReleasedCb), + mBuffersReleased(0) {} + virtual ~DummySurfaceListener() = default; + + virtual void onBufferReleased() { + mBuffersReleased++; + } + virtual bool needsReleaseNotify() { + return mEnableReleaseCb; + } + virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) { + mDiscardedBuffers.insert(mDiscardedBuffers.end(), buffers.begin(), buffers.end()); + } + + int getReleaseNotifyCount() const { + return mBuffersReleased; + } + const std::vector<sp<GraphicBuffer>>& getDiscardedBuffers() const { + return mDiscardedBuffers; + } +private: + // No need to use lock given the test triggers the listener in the same + // thread context. + bool mEnableReleaseCb; + int32_t mBuffersReleased; + std::vector<sp<GraphicBuffer>> mDiscardedBuffers; +}; + class SurfaceTest : public ::testing::Test { protected: SurfaceTest() { @@ -88,6 +119,86 @@ protected: mComposerClient->dispose(); } + void testSurfaceListener(bool hasSurfaceListener, bool enableReleasedCb, + int32_t extraDiscardedBuffers) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<DummyConsumer> dummyConsumer(new DummyConsumer); + consumer->consumerConnect(dummyConsumer, false); + consumer->setConsumerName(String8("TestConsumer")); + + sp<Surface> surface = new Surface(producer); + sp<ANativeWindow> window(surface); + sp<DummySurfaceListener> listener; + if (hasSurfaceListener) { + listener = new DummySurfaceListener(enableReleasedCb); + } + ASSERT_EQ(OK, surface->connect( + NATIVE_WINDOW_API_CPU, + /*reportBufferRemoval*/true, + /*listener*/listener)); + const int BUFFER_COUNT = 4 + extraDiscardedBuffers; + ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); + + ANativeWindowBuffer* buffers[BUFFER_COUNT]; + // Dequeue first to allocate a number of buffers + for (int i = 0; i < BUFFER_COUNT; i++) { + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffers[i])); + } + for (int i = 0; i < BUFFER_COUNT; i++) { + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], -1)); + } + + ANativeWindowBuffer* buffer; + // Fill BUFFER_COUNT-1 buffers + for (int i = 0; i < BUFFER_COUNT-1; i++) { + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffer)); + ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, -1)); + } + + // Dequeue 1 buffer + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffer)); + + // Acquire and free 1+extraDiscardedBuffers buffer, check onBufferReleased is called. + std::vector<BufferItem> releasedItems; + releasedItems.resize(1+extraDiscardedBuffers); + for (int i = 0; i < releasedItems.size(); i++) { + ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&releasedItems[i], 0)); + ASSERT_EQ(NO_ERROR, consumer->releaseBuffer(releasedItems[i].mSlot, + releasedItems[i].mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, + Fence::NO_FENCE)); + } + int32_t expectedReleaseCb = (enableReleasedCb ? releasedItems.size() : 0); + if (hasSurfaceListener) { + ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount()); + } + + // Acquire 1 buffer, leaving 1+extraDiscardedBuffers filled buffer in queue + BufferItem item; + ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&item, 0)); + + // Discard free buffers + ASSERT_EQ(NO_ERROR, consumer->discardFreeBuffers()); + + if (hasSurfaceListener) { + ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount()); + + // Check onBufferDiscarded is called with correct buffer + auto discardedBuffers = listener->getDiscardedBuffers(); + ASSERT_EQ(discardedBuffers.size(), releasedItems.size()); + for (int i = 0; i < releasedItems.size(); i++) { + ASSERT_EQ(discardedBuffers[i], releasedItems[i].mGraphicBuffer); + } + + ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount()); + } + + // Disconnect the surface + ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); + } + sp<Surface> mSurface; sp<SurfaceComposerClient> mComposerClient; sp<SurfaceControl> mSurfaceControl; @@ -480,6 +591,21 @@ TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) { ASSERT_LE(removedBuffers.size(), 1u); } +TEST_F(SurfaceTest, SurfaceListenerTest) { + // Test discarding 1 free buffers with no listener + testSurfaceListener(/*hasListener*/false, /*enableReleaseCb*/false, /*extraDiscardedBuffers*/0); + // Test discarding 2 free buffers with no listener + testSurfaceListener(/*hasListener*/false, /*enableReleaseCb*/false, /*extraDiscardedBuffers*/1); + // Test discarding 1 free buffers with a listener, disabling onBufferReleased + testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/false, /*extraDiscardedBuffers*/0); + // Test discarding 2 free buffers with a listener, disabling onBufferReleased + testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/false, /*extraDiscardedBuffers*/1); + // Test discarding 1 free buffers with a listener, enabling onBufferReleased + testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/true, /*extraDiscardedBuffers*/0); + // Test discarding 3 free buffers with a listener, enabling onBufferReleased + testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/true, /*extraDiscardedBuffers*/2); +} + TEST_F(SurfaceTest, TestGetLastDequeueStartTime) { sp<ANativeWindow> anw(mSurface); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); @@ -548,8 +674,8 @@ public: } sp<ISurfaceComposerClient> createConnection() override { return nullptr; } - sp<IDisplayEventConnection> createDisplayEventConnection(ISurfaceComposer::VsyncSource) - override { + sp<IDisplayEventConnection> createDisplayEventConnection( + ISurfaceComposer::VsyncSource, ISurfaceComposer::ConfigChanged) override { return nullptr; } sp<IBinder> createDisplay(const String8& /*displayName*/, diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp index d6a73bfd2..de3a23d76 100644 --- a/libs/input/IInputFlinger.cpp +++ b/libs/input/IInputFlinger.cpp @@ -45,16 +45,6 @@ public: IBinder::FLAG_ONEWAY); } - virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) { - Parcel data, reply; - data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - - data.writeStrongBinder(fromToken); - data.writeStrongBinder(toToken); - remote()->transact(BnInputFlinger::TRANSFER_TOUCH_FOCUS, data, &reply, - IBinder::FLAG_ONEWAY); - } - virtual void registerInputChannel(const sp<InputChannel>& channel) { Parcel data, reply; data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); @@ -104,13 +94,6 @@ status_t BnInputFlinger::onTransact( unregisterInputChannel(channel); break; } - case TRANSFER_TOUCH_FOCUS: { - CHECK_INTERFACE(IInputFlinger, data, reply); - sp<IBinder> fromToken = data.readStrongBinder(); - sp<IBinder> toToken = data.readStrongBinder(); - transferTouchFocus(fromToken, toToken); - break; - } default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp index 8a4298a36..ce76e3fa6 100644 --- a/libs/input/TouchVideoFrame.cpp +++ b/libs/input/TouchVideoFrame.cpp @@ -42,13 +42,13 @@ const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; void TouchVideoFrame::rotate(int32_t orientation) { switch (orientation) { case DISPLAY_ORIENTATION_90: - rotateQuarterTurn(true /*clockwise*/); + rotateQuarterTurn(false /*clockwise*/); break; case DISPLAY_ORIENTATION_180: rotate180(); break; case DISPLAY_ORIENTATION_270: - rotateQuarterTurn(false /*clockwise*/); + rotateQuarterTurn(true /*clockwise*/); break; } } diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp index 815424ee3..fe06cad72 100644 --- a/libs/input/tests/TouchVideoFrame_test.cpp +++ b/libs/input/tests/TouchVideoFrame_test.cpp @@ -85,14 +85,14 @@ TEST(TouchVideoFrame, Rotate90_1x1) { TEST(TouchVideoFrame, Rotate90_2x2) { TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); - TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_90); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate90_3x2) { TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); - TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_90); ASSERT_EQ(frame, frameRotated); } @@ -170,14 +170,14 @@ TEST(TouchVideoFrame, Rotate270_1x1) { TEST(TouchVideoFrame, Rotate270_2x2) { TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); - TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_270); ASSERT_EQ(frame, frameRotated); } TEST(TouchVideoFrame, Rotate270_3x2) { TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); - TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP); frame.rotate(DISPLAY_ORIENTATION_270); ASSERT_EQ(frame, frameRotated); } diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 36211ca73..cc252d67a 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -26,6 +26,7 @@ cc_defaults { "libgui", "liblog", "libnativewindow", + "libprocessgroup", "libsync", "libui", "libutils", @@ -51,6 +52,7 @@ filegroup { "gl/GLExtensions.cpp", "gl/GLFramebuffer.cpp", "gl/GLImage.cpp", + "gl/ImageManager.cpp", "gl/Program.cpp", "gl/ProgramCache.cpp", ], diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 46a8e9eec..d2a752511 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -19,9 +19,8 @@ #define LOG_TAG "RenderEngine" #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include "GLESRenderEngine.h" - -#include <math.h> +#include <sched.h> +#include <cmath> #include <fstream> #include <sstream> #include <unordered_set> @@ -31,6 +30,7 @@ #include <android-base/stringprintf.h> #include <cutils/compiler.h> #include <cutils/properties.h> +#include <gui/DebugEGLImageTracker.h> #include <renderengine/Mesh.h> #include <renderengine/Texture.h> #include <renderengine/private/Description.h> @@ -42,6 +42,7 @@ #include <ui/Region.h> #include <utils/KeyedVector.h> #include <utils/Trace.h> +#include "GLESRenderEngine.h" #include "GLExtensions.h" #include "GLFramebuffer.h" #include "GLImage.h" @@ -422,10 +423,13 @@ GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EG mTraceGpuCompletion = true; mFlushTracer = std::make_unique<FlushTracer>(this); } + mImageManager = std::make_unique<ImageManager>(this); mDrawingBuffer = createFramebuffer(); } GLESRenderEngine::~GLESRenderEngine() { + // Destroy the image manager first. + mImageManager = nullptr; std::lock_guard<std::mutex> lock(mRenderingMutex); unbindFrameBuffer(mDrawingBuffer.get()); mDrawingBuffer = nullptr; @@ -433,6 +437,7 @@ GLESRenderEngine::~GLESRenderEngine() { EGLImageKHR expired = mFramebufferImageCache.front().second; mFramebufferImageCache.pop_front(); eglDestroyImageKHR(mEGLDisplay, expired); + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); } mImageCache.clear(); eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); @@ -614,64 +619,51 @@ void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& i } } -status_t GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { - std::lock_guard<std::mutex> lock(mRenderingMutex); - return cacheExternalTextureBufferLocked(buffer); -} - status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, const sp<Fence>& bufferFence) { - std::lock_guard<std::mutex> lock(mRenderingMutex); - return bindExternalTextureBufferLocked(texName, buffer, bufferFence); -} - -status_t GLESRenderEngine::cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) { if (buffer == nullptr) { return BAD_VALUE; } ATRACE_CALL(); - if (mImageCache.count(buffer->getId()) > 0) { - return NO_ERROR; + bool found = false; + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + auto cachedImage = mImageCache.find(buffer->getId()); + found = (cachedImage != mImageCache.end()); } - std::unique_ptr<Image> newImage = createImage(); - - bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(), - buffer->getUsage() & GRALLOC_USAGE_PROTECTED); - if (!created) { - ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", - buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), - buffer->getPixelFormat()); - return NO_INIT; + // If we couldn't find the image in the cache at this time, then either + // SurfaceFlinger messed up registering the buffer ahead of time or we got + // backed up creating other EGLImages. + if (!found) { + status_t cacheResult = mImageManager->cache(buffer); + if (cacheResult != NO_ERROR) { + return cacheResult; + } } - mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage))); - return NO_ERROR; -} - -status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName, - const sp<GraphicBuffer>& buffer, - const sp<Fence>& bufferFence) { - ATRACE_CALL(); - status_t cacheResult = cacheExternalTextureBufferLocked(buffer); - - if (cacheResult != NO_ERROR) { - return cacheResult; - } + // Whether or not we needed to cache, re-check mImageCache to make sure that + // there's an EGLImage. The current threading model guarantees that we don't + // destroy a cached image until it's really not needed anymore (i.e. this + // function should not be called), so the only possibility is that something + // terrible went wrong and we should just bind something and move on. + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + auto cachedImage = mImageCache.find(buffer->getId()); - auto cachedImage = mImageCache.find(buffer->getId()); + if (cachedImage == mImageCache.end()) { + // We failed creating the image if we got here, so bail out. + ALOGE("Failed to create an EGLImage when rendering"); + bindExternalTextureImage(texName, *createImage()); + return NO_INIT; + } - if (cachedImage == mImageCache.end()) { - // We failed creating the image if we got here, so bail out. - bindExternalTextureImage(texName, *createImage()); - return NO_INIT; + bindExternalTextureImage(texName, *cachedImage->second); } - bindExternalTextureImage(texName, *cachedImage->second); - // Wait for the new buffer to be ready. if (bufferFence != nullptr && bufferFence->isValid()) { if (GLExtensions::getInstance().hasWaitSync()) { @@ -696,13 +688,81 @@ status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName, return NO_ERROR; } +void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { + mImageManager->cacheAsync(buffer, nullptr); +} + +std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::cacheExternalTextureBufferForTesting( + const sp<GraphicBuffer>& buffer) { + auto barrier = std::make_shared<ImageManager::Barrier>(); + mImageManager->cacheAsync(buffer, barrier); + return barrier; +} + +status_t GLESRenderEngine::cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) { + if (buffer == nullptr) { + return BAD_VALUE; + } + + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + if (mImageCache.count(buffer->getId()) > 0) { + // If there's already an image then fail fast here. + return NO_ERROR; + } + } + ATRACE_CALL(); + + // Create the image without holding a lock so that we don't block anything. + std::unique_ptr<Image> newImage = createImage(); + + bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(), + buffer->getUsage() & GRALLOC_USAGE_PROTECTED); + if (!created) { + ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", + buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), + buffer->getPixelFormat()); + return NO_INIT; + } + + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + if (mImageCache.count(buffer->getId()) > 0) { + // In theory it's possible for another thread to recache the image, + // so bail out if another thread won. + return NO_ERROR; + } + mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage))); + } + + return NO_ERROR; +} + void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) { - std::lock_guard<std::mutex> lock(mRenderingMutex); - const auto& cachedImage = mImageCache.find(bufferId); - if (cachedImage != mImageCache.end()) { - ALOGV("Destroying image for buffer: %" PRIu64, bufferId); - mImageCache.erase(bufferId); - return; + mImageManager->releaseAsync(bufferId, nullptr); +} + +std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::unbindExternalTextureBufferForTesting( + uint64_t bufferId) { + auto barrier = std::make_shared<ImageManager::Barrier>(); + mImageManager->releaseAsync(bufferId, barrier); + return barrier; +} + +void GLESRenderEngine::unbindExternalTextureBufferInternal(uint64_t bufferId) { + std::unique_ptr<Image> image; + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + const auto& cachedImage = mImageCache.find(bufferId); + + if (cachedImage != mImageCache.end()) { + ALOGV("Destroying image for buffer: %" PRIu64, bufferId); + // Move the buffer out of cache first, so that we can destroy + // without holding the cache's lock. + image = std::move(cachedImage->second); + mImageCache.erase(bufferId); + return; + } } ALOGV("Failed to find image for buffer: %" PRIu64, bufferId); } @@ -842,6 +902,7 @@ EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer bool useFramebufferCache) { sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer); if (useFramebufferCache) { + std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); for (const auto& image : mFramebufferImageCache) { if (image.first == graphicBuffer->getId()) { return image.second; @@ -857,14 +918,20 @@ EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer nativeBuffer, attributes); if (useFramebufferCache) { if (image != EGL_NO_IMAGE_KHR) { + std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) { EGLImageKHR expired = mFramebufferImageCache.front().second; mFramebufferImageCache.pop_front(); eglDestroyImageKHR(mEGLDisplay, expired); + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); } mFramebufferImageCache.push_back({graphicBuffer->getId(), image}); } } + + if (image != EGL_NO_IMAGE_KHR) { + DEBUG_EGL_IMAGE_TRACKER_CREATE(); + } return image; } @@ -889,127 +956,123 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, return BAD_VALUE; } - { - std::lock_guard<std::mutex> lock(mRenderingMutex); + BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache); - BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache); - - if (fbo.getStatus() != NO_ERROR) { - ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", - buffer->handle); - checkErrors(); - return fbo.getStatus(); - } - - // clear the entire buffer, sometimes when we reuse buffers we'd persist - // ghost images otherwise. - // we also require a full transparent framebuffer for overlays. This is - // probably not quite efficient on all GPUs, since we could filter out - // opaque layers. - clearWithColor(0.0, 0.0, 0.0, 0.0); + if (fbo.getStatus() != NO_ERROR) { + ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", + buffer->handle); + checkErrors(); + return fbo.getStatus(); + } - setViewportAndProjection(display.physicalDisplay, display.clip); + // clear the entire buffer, sometimes when we reuse buffers we'd persist + // ghost images otherwise. + // we also require a full transparent framebuffer for overlays. This is + // probably not quite efficient on all GPUs, since we could filter out + // opaque layers. + clearWithColor(0.0, 0.0, 0.0, 0.0); - setOutputDataSpace(display.outputDataspace); - setDisplayMaxLuminance(display.maxLuminance); + setViewportAndProjection(display.physicalDisplay, display.clip); - mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform; - mState.projectionMatrix = projectionMatrix; - if (!display.clearRegion.isEmpty()) { - glDisable(GL_BLEND); - fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0); - } + setOutputDataSpace(display.outputDataspace); + setDisplayMaxLuminance(display.maxLuminance); - Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2); - for (auto layer : layers) { - mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; + mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform; + mState.projectionMatrix = projectionMatrix; + if (!display.clearRegion.isEmpty()) { + glDisable(GL_BLEND); + fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0); + } - const FloatRect bounds = layer.geometry.boundaries; - Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); - position[0] = vec2(bounds.left, bounds.top); - position[1] = vec2(bounds.left, bounds.bottom); - position[2] = vec2(bounds.right, bounds.bottom); - position[3] = vec2(bounds.right, bounds.top); + Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2); + for (auto layer : layers) { + mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; - setupLayerCropping(layer, mesh); - setColorTransform(display.colorTransform * layer.colorTransform); + const FloatRect bounds = layer.geometry.boundaries; + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + position[0] = vec2(bounds.left, bounds.top); + position[1] = vec2(bounds.left, bounds.bottom); + position[2] = vec2(bounds.right, bounds.bottom); + position[3] = vec2(bounds.right, bounds.top); - bool usePremultipliedAlpha = true; - bool disableTexture = true; - bool isOpaque = false; + setupLayerCropping(layer, mesh); + setColorTransform(display.colorTransform * layer.colorTransform); - if (layer.source.buffer.buffer != nullptr) { - disableTexture = false; - isOpaque = layer.source.buffer.isOpaque; + bool usePremultipliedAlpha = true; + bool disableTexture = true; + bool isOpaque = false; - sp<GraphicBuffer> gBuf = layer.source.buffer.buffer; - bindExternalTextureBufferLocked(layer.source.buffer.textureName, gBuf, - layer.source.buffer.fence); + if (layer.source.buffer.buffer != nullptr) { + disableTexture = false; + isOpaque = layer.source.buffer.isOpaque; - usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha; - Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName); - mat4 texMatrix = layer.source.buffer.textureTransform; + sp<GraphicBuffer> gBuf = layer.source.buffer.buffer; + bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf, + layer.source.buffer.fence); - texture.setMatrix(texMatrix.asArray()); - texture.setFiltering(layer.source.buffer.useTextureFiltering); + usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha; + Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName); + mat4 texMatrix = layer.source.buffer.textureTransform; - texture.setDimensions(gBuf->getWidth(), gBuf->getHeight()); - setSourceY410BT2020(layer.source.buffer.isY410BT2020); + texture.setMatrix(texMatrix.asArray()); + texture.setFiltering(layer.source.buffer.useTextureFiltering); - renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>()); - texCoords[0] = vec2(0.0, 0.0); - texCoords[1] = vec2(0.0, 1.0); - texCoords[2] = vec2(1.0, 1.0); - texCoords[3] = vec2(1.0, 0.0); - setupLayerTexturing(texture); - } + texture.setDimensions(gBuf->getWidth(), gBuf->getHeight()); + setSourceY410BT2020(layer.source.buffer.isY410BT2020); - const half3 solidColor = layer.source.solidColor; - const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha); - // Buffer sources will have a black solid color ignored in the shader, - // so in that scenario the solid color passed here is arbitrary. - setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, - layer.geometry.roundedCornersRadius); - if (layer.disableBlending) { - glDisable(GL_BLEND); - } - setSourceDataSpace(layer.sourceDataspace); - - // We only want to do a special handling for rounded corners when having rounded corners - // is the only reason it needs to turn on blending, otherwise, we handle it like the - // usual way since it needs to turn on blending anyway. - if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) { - handleRoundedCorners(display, layer, mesh); - } else { - drawMesh(mesh); - } + renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>()); + texCoords[0] = vec2(0.0, 0.0); + texCoords[1] = vec2(0.0, 1.0); + texCoords[2] = vec2(1.0, 1.0); + texCoords[3] = vec2(1.0, 0.0); + setupLayerTexturing(texture); + } - // Cleanup if there's a buffer source - if (layer.source.buffer.buffer != nullptr) { - disableBlending(); - setSourceY410BT2020(false); - disableTexturing(); - } + const half3 solidColor = layer.source.solidColor; + const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha); + // Buffer sources will have a black solid color ignored in the shader, + // so in that scenario the solid color passed here is arbitrary. + setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, + layer.geometry.roundedCornersRadius); + if (layer.disableBlending) { + glDisable(GL_BLEND); } + setSourceDataSpace(layer.sourceDataspace); - if (drawFence != nullptr) { - *drawFence = flush(); + // We only want to do a special handling for rounded corners when having rounded corners + // is the only reason it needs to turn on blending, otherwise, we handle it like the + // usual way since it needs to turn on blending anyway. + if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) { + handleRoundedCorners(display, layer, mesh); + } else { + drawMesh(mesh); } - // If flush failed or we don't support native fences, we need to force the - // gl command stream to be executed. - if (drawFence == nullptr || drawFence->get() < 0) { - bool success = finish(); - if (!success) { - ALOGE("Failed to flush RenderEngine commands"); - checkErrors(); - // Chances are, something illegal happened (either the caller passed - // us bad parameters, or we messed up our shader generation). - return INVALID_OPERATION; - } + + // Cleanup if there's a buffer source + if (layer.source.buffer.buffer != nullptr) { + disableBlending(); + setSourceY410BT2020(false); + disableTexturing(); } + } - checkErrors(); + if (drawFence != nullptr) { + *drawFence = flush(); } + // If flush failed or we don't support native fences, we need to force the + // gl command stream to be executed. + if (drawFence == nullptr || drawFence->get() < 0) { + bool success = finish(); + if (!success) { + ALOGE("Failed to flush RenderEngine commands"); + checkErrors(); + // Chances are, something illegal happened (either the caller passed + // us bad parameters, or we messed up our shader generation). + return INVALID_OPERATION; + } + } + + checkErrors(); return NO_ERROR; } @@ -1306,6 +1369,23 @@ void GLESRenderEngine::dump(std::string& result) { StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n", dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(), dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str()); + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + StringAppendF(&result, "RenderEngine image cache size: %zu\n", mImageCache.size()); + StringAppendF(&result, "Dumping buffer ids...\n"); + for (const auto& [id, unused] : mImageCache) { + StringAppendF(&result, "0x%" PRIx64 "\n", id); + } + } + { + std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); + StringAppendF(&result, "RenderEngine framebuffer image cache size: %zu\n", + mFramebufferImageCache.size()); + StringAppendF(&result, "Dumping buffer ids...\n"); + for (const auto& [id, unused] : mFramebufferImageCache) { + StringAppendF(&result, "0x%" PRIx64 "\n", id); + } + } } GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) { @@ -1432,7 +1512,7 @@ bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) { } bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) { - std::lock_guard<std::mutex> lock(mRenderingMutex); + std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(), [=](std::pair<uint64_t, EGLImageKHR> image) { return image.first == bufferId; diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index de793c214..dd60e50c6 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -17,9 +17,7 @@ #ifndef SF_GLESRENDERENGINE_H_ #define SF_GLESRENDERENGINE_H_ -#include <android-base/thread_annotations.h> #include <stdint.h> -#include <sys/types.h> #include <condition_variable> #include <deque> #include <mutex> @@ -30,8 +28,11 @@ #include <EGL/egl.h> #include <EGL/eglext.h> #include <GLES2/gl2.h> +#include <android-base/thread_annotations.h> #include <renderengine/RenderEngine.h> #include <renderengine/private/Description.h> +#include <sys/types.h> +#include "ImageManager.h" #define EGL_NO_CONFIG ((EGLConfig)0) @@ -74,7 +75,7 @@ public: void bindExternalTextureImage(uint32_t texName, const Image& image) override; status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, const sp<Fence>& fence) EXCLUDES(mRenderingMutex); - status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); + void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex); status_t bindFrameBuffer(Framebuffer* framebuffer) override; void unbindFrameBuffer(Framebuffer* framebuffer) override; @@ -85,25 +86,32 @@ public: bool useProtectedContext(bool useProtectedContext) override; status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers, ANativeWindowBuffer* buffer, const bool useFramebufferCache, - base::unique_fd&& bufferFence, base::unique_fd* drawFence) - EXCLUDES(mRenderingMutex) override; + base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; // internal to RenderEngine EGLDisplay getEGLDisplay() const { return mEGLDisplay; } EGLConfig getEGLConfig() const { return mEGLConfig; } // Creates an output image for rendering to EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected, - bool useFramebufferCache); + bool useFramebufferCache) + EXCLUDES(mFramebufferImageCacheMutex); // Test-only methods // Returns true iff mImageCache contains an image keyed by bufferId bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex); // Returns true iff mFramebufferImageCache contains an image keyed by bufferId - bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex); + bool isFramebufferImageCachedForTesting(uint64_t bufferId) + EXCLUDES(mFramebufferImageCacheMutex); + // These are wrappers around public methods above, but exposing Barrier + // objects so that tests can block. + std::shared_ptr<ImageManager::Barrier> cacheExternalTextureBufferForTesting( + const sp<GraphicBuffer>& buffer); + std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId); protected: Framebuffer* getFramebufferForDrawing() override; - void dump(std::string& result) override; + void dump(std::string& result) override EXCLUDES(mRenderingMutex) + EXCLUDES(mFramebufferImageCacheMutex); void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, ui::Transform::orientation_flags rotation) override; void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, @@ -145,6 +153,9 @@ private: void setScissor(const Rect& region); void disableScissor(); bool waitSync(EGLSyncKHR sync, EGLint flags); + status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) + EXCLUDES(mRenderingMutex); + void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex); // A data space is considered HDR data space if it has BT2020 color space // with PQ or HLG transfer function. @@ -200,7 +211,11 @@ private: uint32_t mFramebufferImageCacheSize = 0; // Cache of output images, keyed by corresponding GraphicBuffer ID. - std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache; + std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache + GUARDED_BY(mFramebufferImageCacheMutex); + // The only reason why we have this mutex is so that we don't segfault when + // dumping info. + std::mutex mFramebufferImageCacheMutex; // Current dataspace of layer being rendered ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN; @@ -220,15 +235,6 @@ private: // multiple threads is guaranteed thread-safe. std::mutex mRenderingMutex; - // See bindExternalTextureBuffer above, but requiring that mRenderingMutex - // is held. - status_t bindExternalTextureBufferLocked(uint32_t texName, const sp<GraphicBuffer>& buffer, - const sp<Fence>& fence) REQUIRES(mRenderingMutex); - // See cacheExternalTextureBuffer above, but requiring that mRenderingMutex - // is held. - status_t cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) - REQUIRES(mRenderingMutex); - std::unique_ptr<Framebuffer> mDrawingBuffer; class FlushTracer { @@ -253,7 +259,9 @@ private: bool mRunning = true; }; friend class FlushTracer; + friend class ImageManager; std::unique_ptr<FlushTracer> mFlushTracer; + std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this); }; } // namespace gl diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp index dacf8d3d8..5fbb5ba7d 100644 --- a/libs/renderengine/gl/GLFramebuffer.cpp +++ b/libs/renderengine/gl/GLFramebuffer.cpp @@ -22,6 +22,7 @@ #include <GLES/glext.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include <gui/DebugEGLImageTracker.h> #include <nativebase/nativebase.h> #include <utils/Trace.h> #include "GLESRenderEngine.h" @@ -47,6 +48,7 @@ bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, boo if (mEGLImage != EGL_NO_IMAGE_KHR) { if (!usingFramebufferCache) { eglDestroyImageKHR(mEGLDisplay, mEGLImage); + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); } mEGLImage = EGL_NO_IMAGE_KHR; mBufferWidth = 0; diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp index 77e648e70..849772195 100644 --- a/libs/renderengine/gl/GLImage.cpp +++ b/libs/renderengine/gl/GLImage.cpp @@ -20,6 +20,7 @@ #include <vector> +#include <gui/DebugEGLImageTracker.h> #include <log/log.h> #include <utils/Trace.h> #include "GLESRenderEngine.h" @@ -58,6 +59,7 @@ bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtecte if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) { ALOGE("failed to destroy image: %#x", eglGetError()); } + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); mEGLImage = EGL_NO_IMAGE_KHR; } @@ -69,6 +71,7 @@ bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtecte ALOGE("failed to create EGLImage: %#x", eglGetError()); return false; } + DEBUG_EGL_IMAGE_TRACKER_CREATE(); mProtected = isProtected; } diff --git a/libs/renderengine/gl/ImageManager.cpp b/libs/renderengine/gl/ImageManager.cpp new file mode 100644 index 000000000..5af0e4f85 --- /dev/null +++ b/libs/renderengine/gl/ImageManager.cpp @@ -0,0 +1,140 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <pthread.h> + +#include <processgroup/sched_policy.h> +#include <utils/Trace.h> +#include "GLESRenderEngine.h" +#include "ImageManager.h" + +namespace android { +namespace renderengine { +namespace gl { + +ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) { + pthread_setname_np(mThread.native_handle(), "ImageManager"); + // Use SCHED_FIFO to minimize jitter + struct sched_param param = {0}; + param.sched_priority = 2; + if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for ImageManager"); + } +} + +ImageManager::~ImageManager() { + { + std::lock_guard<std::mutex> lock(mMutex); + mRunning = false; + } + mCondition.notify_all(); + if (mThread.joinable()) { + mThread.join(); + } +} + +void ImageManager::cacheAsync(const sp<GraphicBuffer>& buffer, + const std::shared_ptr<Barrier>& barrier) { + if (buffer == nullptr) { + { + std::lock_guard<std::mutex> lock(barrier->mutex); + barrier->isOpen = true; + barrier->result = BAD_VALUE; + } + barrier->condition.notify_one(); + return; + } + ATRACE_CALL(); + QueueEntry entry = {QueueEntry::Operation::Insert, buffer, buffer->getId(), barrier}; + queueOperation(std::move(entry)); +} + +status_t ImageManager::cache(const sp<GraphicBuffer>& buffer) { + ATRACE_CALL(); + auto barrier = std::make_shared<Barrier>(); + cacheAsync(buffer, barrier); + std::lock_guard<std::mutex> lock(barrier->mutex); + barrier->condition.wait(barrier->mutex, + [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; }); + return barrier->result; +} + +void ImageManager::releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) { + ATRACE_CALL(); + QueueEntry entry = {QueueEntry::Operation::Delete, nullptr, bufferId, barrier}; + queueOperation(std::move(entry)); +} + +void ImageManager::queueOperation(const QueueEntry&& entry) { + { + std::lock_guard<std::mutex> lock(mMutex); + mQueue.emplace(entry); + ATRACE_INT("ImageManagerQueueDepth", mQueue.size()); + } + mCondition.notify_one(); +} + +void ImageManager::threadMain() { + set_sched_policy(0, SP_FOREGROUND); + bool run; + { + std::lock_guard<std::mutex> lock(mMutex); + run = mRunning; + } + while (run) { + QueueEntry entry; + { + std::lock_guard<std::mutex> lock(mMutex); + mCondition.wait(mMutex, + [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; }); + run = mRunning; + + if (!mRunning) { + // if mRunning is false, then ImageManager is being destroyed, so + // bail out now. + break; + } + + entry = mQueue.front(); + mQueue.pop(); + ATRACE_INT("ImageManagerQueueDepth", mQueue.size()); + } + + status_t result = NO_ERROR; + switch (entry.op) { + case QueueEntry::Operation::Delete: + mEngine->unbindExternalTextureBufferInternal(entry.bufferId); + break; + case QueueEntry::Operation::Insert: + result = mEngine->cacheExternalTextureBufferInternal(entry.buffer); + break; + } + if (entry.barrier != nullptr) { + { + std::lock_guard<std::mutex> entryLock(entry.barrier->mutex); + entry.barrier->result = result; + entry.barrier->isOpen = true; + } + entry.barrier->condition.notify_one(); + } + } +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/ImageManager.h b/libs/renderengine/gl/ImageManager.h new file mode 100644 index 000000000..b5ba554c4 --- /dev/null +++ b/libs/renderengine/gl/ImageManager.h @@ -0,0 +1,70 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <condition_variable> +#include <mutex> +#include <queue> +#include <thread> + +#include <ui/GraphicBuffer.h> + +namespace android { +namespace renderengine { +namespace gl { + +class GLESRenderEngine; + +class ImageManager { +public: + struct Barrier { + std::mutex mutex; + std::condition_variable_any condition; + bool isOpen GUARDED_BY(mutex) = false; + status_t result GUARDED_BY(mutex) = NO_ERROR; + }; + ImageManager(GLESRenderEngine* engine); + ~ImageManager(); + void cacheAsync(const sp<GraphicBuffer>& buffer, const std::shared_ptr<Barrier>& barrier) + EXCLUDES(mMutex); + status_t cache(const sp<GraphicBuffer>& buffer); + void releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) EXCLUDES(mMutex); + +private: + struct QueueEntry { + enum class Operation { Delete, Insert }; + + Operation op = Operation::Delete; + sp<GraphicBuffer> buffer = nullptr; + uint64_t bufferId = 0; + std::shared_ptr<Barrier> barrier = nullptr; + }; + + void queueOperation(const QueueEntry&& entry); + void threadMain(); + GLESRenderEngine* const mEngine; + std::thread mThread = std::thread([this]() { threadMain(); }); + std::condition_variable_any mCondition; + std::mutex mMutex; + std::queue<QueueEntry> mQueue GUARDED_BY(mMutex); + + bool mRunning GUARDED_BY(mMutex) = true; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index e7070041f..c6a7bd843 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -116,10 +116,20 @@ public: const sp<Fence>& fence) = 0; // Caches Image resources for this buffer, but does not bind the buffer to // a particular texture. - virtual status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0; + // Note that work is deferred to an additional thread, i.e. this call + // is made asynchronously, but the caller can expect that cache/unbind calls + // are performed in a manner that's conflict serializable, i.e. unbinding + // a buffer should never occur before binding the buffer if the caller + // called {bind, cache}ExternalTextureBuffer before calling unbind. + virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0; // Removes internal resources referenced by the bufferId. This method should be // invoked when the caller will no longer hold a reference to a GraphicBuffer // and needs to clean up its resources. + // Note that work is deferred to an additional thread, i.e. this call + // is made asynchronously, but the caller can expect that cache/unbind calls + // are performed in a manner that's conflict serializable, i.e. unbinding + // a buffer should never occur before binding the buffer if the caller + // called {bind, cache}ExternalTextureBuffer before calling unbind. virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0; // When binding a native buffer, it must be done before setViewportAndProjection // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation. @@ -176,6 +186,17 @@ public: // should be called for every display that needs to be rendered via the GPU. // @param display The display-wide settings that should be applied prior to // drawing any layers. + // + // Assumptions when calling this method: + // 1. There is exactly one caller - i.e. multi-threading is not supported. + // 2. Additional threads may be calling the {bind,cache}ExternalTexture + // methods above. But the main thread is responsible for holding resources + // such that Image destruction does not occur while this method is called. + // + // TODO(b/136806342): This should behavior should ideally be fixed since + // the above two assumptions are brittle, as conditional thread safetyness + // may be insufficient when maximizing rendering performance in the future. + // // @param layers The layers to draw onto the display, in Z-order. // @param buffer The buffer which will be drawn to. This buffer will be // ready once drawFence fires. diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index e33bcfd99..b4d3ef24b 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -51,7 +51,7 @@ public: MOCK_METHOD2(genTextures, void(size_t, uint32_t*)); MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*)); MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&)); - MOCK_METHOD1(cacheExternalTextureBuffer, status_t(const sp<GraphicBuffer>&)); + MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp<GraphicBuffer>&)); MOCK_METHOD3(bindExternalTextureBuffer, status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&)); MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t)); diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index 9b483ef51..e98babc30 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -31,6 +31,7 @@ cc_test { "libgui", "liblog", "libnativewindow", + "libprocessgroup", "libsync", "libui", "libutils", diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 7acaecf46..f47c7fd05 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -14,8 +14,10 @@ * limitations under the License. */ -#include <gtest/gtest.h> +#include <chrono> +#include <condition_variable> +#include <gtest/gtest.h> #include <renderengine/RenderEngine.h> #include <sync/sync.h> #include <ui/PixelFormat.h> @@ -1001,8 +1003,15 @@ TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) { invokeDraw(settings, layers, mBuffer); uint64_t bufferId = layer.source.buffer.buffer->getId(); EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); - sRE->unbindExternalTextureBuffer(bufferId); + std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = + sRE->unbindExternalTextureBufferForTesting(bufferId); + std::lock_guard<std::mutex> lock(barrier->mutex); + ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), + [&]() REQUIRES(barrier->mutex) { + return barrier->isOpen; + })); EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); + EXPECT_EQ(NO_ERROR, barrier->result); } TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) { @@ -1019,21 +1028,52 @@ TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) { sRE->bindExternalTextureBuffer(texName, buf, nullptr); uint64_t bufferId = buf->getId(); EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); - sRE->unbindExternalTextureBuffer(bufferId); + std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = + sRE->unbindExternalTextureBufferForTesting(bufferId); + std::lock_guard<std::mutex> lock(barrier->mutex); + ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), + [&]() REQUIRES(barrier->mutex) { + return barrier->isOpen; + })); + EXPECT_EQ(NO_ERROR, barrier->result); EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); } TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferWithNullBuffer) { - status_t result = sRE->cacheExternalTextureBuffer(nullptr); - ASSERT_EQ(BAD_VALUE, result); + std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = + sRE->cacheExternalTextureBufferForTesting(nullptr); + std::lock_guard<std::mutex> lock(barrier->mutex); + ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), + [&]() REQUIRES(barrier->mutex) { + return barrier->isOpen; + })); + EXPECT_TRUE(barrier->isOpen); + EXPECT_EQ(BAD_VALUE, barrier->result); } TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferCachesImages) { sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); uint64_t bufferId = buf->getId(); - sRE->cacheExternalTextureBuffer(buf); + std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = + sRE->cacheExternalTextureBufferForTesting(buf); + { + std::lock_guard<std::mutex> lock(barrier->mutex); + ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), + [&]() REQUIRES(barrier->mutex) { + return barrier->isOpen; + })); + EXPECT_EQ(NO_ERROR, barrier->result); + } EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); - sRE->unbindExternalTextureBuffer(bufferId); + barrier = sRE->unbindExternalTextureBufferForTesting(bufferId); + { + std::lock_guard<std::mutex> lock(barrier->mutex); + ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), + [&]() REQUIRES(barrier->mutex) { + return barrier->isOpen; + })); + EXPECT_EQ(NO_ERROR, barrier->result); + } EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); } diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 0861a1f9a..d588346df 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -20,6 +20,7 @@ #include <ui/GraphicBufferAllocator.h> +#include <limits.h> #include <stdio.h> #include <grallocusage/GrallocUsageConversion.h> @@ -114,6 +115,14 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, if (!width || !height) width = height = 1; + const uint32_t bpp = bytesPerPixel(format); + if (std::numeric_limits<size_t>::max() / width / height < static_cast<size_t>(bpp)) { + ALOGE("Failed to allocate (%u x %u) layerCount %u format %d " + "usage %" PRIx64 ": Requesting too large a buffer size", + width, height, layerCount, format, usage); + return BAD_VALUE; + } + // Ensure that layerCount is valid. if (layerCount < 1) layerCount = 1; @@ -123,10 +132,20 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, status_t error = mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle); + size_t bufSize; + + // if stride has no meaning or is too large, + // approximate size with the input width instead + if ((*stride) != 0 && + std::numeric_limits<size_t>::max() / height / (*stride) < static_cast<size_t>(bpp)) { + bufSize = static_cast<size_t>(width) * height * bpp; + } else { + bufSize = static_cast<size_t>((*stride)) * height * bpp; + } + if (error == NO_ERROR) { Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); - uint32_t bpp = bytesPerPixel(format); alloc_rec_t rec; rec.width = width; rec.height = height; @@ -134,7 +153,7 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, rec.format = format; rec.layerCount = layerCount; rec.usage = usage; - rec.size = static_cast<size_t>(height * (*stride) * bpp); + rec.size = bufSize; rec.requestorName = std::move(requestorName); list.add(*handle, rec); diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h index 25d451285..324d9e143 100644 --- a/libs/ui/include/ui/GraphicBufferAllocator.h +++ b/libs/ui/include/ui/GraphicBufferAllocator.h @@ -54,7 +54,7 @@ public: void dump(std::string& res) const; static void dumpToSystemLog(); -private: +protected: struct alloc_rec_t { uint32_t width; uint32_t height; diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 373fa4f22..15f4b6407 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -29,6 +29,26 @@ cc_test { } cc_test { + name: "GraphicBufferAllocator_test", + header_libs: [ + "libdvr_headers", + "libnativewindow_headers", + ], + static_libs: [ + "libgmock", + ], + shared_libs: [ + "liblog", + "libui", + ], + srcs: [ + "GraphicBufferAllocator_test.cpp", + "mock/MockGrallocAllocator.cpp", + ], + cflags: ["-Wall", "-Werror"], +} + +cc_test { name: "GraphicBuffer_test", header_libs: [ "libdvr_headers", diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp new file mode 100644 index 000000000..efca083e6 --- /dev/null +++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "GraphicBufferAllocatorTest" + +#include <ui/GraphicBuffer.h> +#include <ui/GraphicBufferAllocator.h> +#include <ui/PixelFormat.h> + +#include <gtest/gtest.h> + +#include "mock/MockGrallocAllocator.h" + +#include <algorithm> +#include <limits> + +namespace android { + +namespace { + +constexpr uint32_t kTestWidth = 1024; +constexpr uint32_t kTestHeight = 1; +constexpr uint32_t kTestLayerCount = 1; +constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN; + +} // namespace + +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SetArgPointee; + +class TestableGraphicBufferAllocator : public GraphicBufferAllocator { +public: + TestableGraphicBufferAllocator() { + mAllocator = std::make_unique<const mock::MockGrallocAllocator>(); + } + void setUpAllocateExpectations(status_t err, uint32_t stride) { + std::cout << "Setting expected stride to " << stride << std::endl; + EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())), + allocate) + .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err))); + } + std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; } +}; + +class GraphicBufferAllocatorTest : public testing::Test { +public: + GraphicBufferAllocatorTest() : mAllocator() {} + const TestableGraphicBufferAllocator& getAllocator() { return mAllocator; } + +protected: + TestableGraphicBufferAllocator mAllocator; +}; + +TEST_F(GraphicBufferAllocatorTest, AllocateNoError) { + mAllocator.setUpAllocateExpectations(NO_ERROR, kTestWidth); + android::PixelFormat format = PIXEL_FORMAT_RGBA_8888; + uint32_t stride = 0; + buffer_handle_t handle; + status_t err = mAllocator.allocate(kTestWidth, kTestHeight, format, kTestLayerCount, kTestUsage, + &handle, &stride, 0, "GraphicBufferAllocatorTest"); + ASSERT_EQ(NO_ERROR, err); + ASSERT_EQ(kTestWidth, stride); +} + +TEST_F(GraphicBufferAllocatorTest, AllocateZeroStride) { + android::PixelFormat format = PIXEL_FORMAT_RGBA_8888; + uint32_t expectedStride = 0; + + mAllocator.setUpAllocateExpectations(NO_ERROR, expectedStride); + uint32_t stride = 0; + buffer_handle_t handle; + // a divide by zero would cause a crash + status_t err = mAllocator.allocate(kTestWidth, kTestHeight, format, kTestLayerCount, kTestUsage, + &handle, &stride, 0, "GraphicBufferAllocatorTest"); + ASSERT_EQ(NO_ERROR, err); + ASSERT_EQ(expectedStride, stride); +} + +TEST_F(GraphicBufferAllocatorTest, AllocateLargeStride) { + uint32_t height = std::numeric_limits<uint32_t>::max(); + uint32_t bpp = 4; + android::PixelFormat format = PIXEL_FORMAT_RGBA_8888; + + if (std::numeric_limits<size_t>::max() / height / bpp >= std::numeric_limits<uint32_t>::max()) { + std::cout << "stride cannot cause overflow" << std::endl; + GTEST_SUCCEED() << "stride cannot cause overflow"; + return; + } + uint32_t width = std::numeric_limits<size_t>::max() / height / bpp; + + uint32_t expectedStride = std::numeric_limits<uint32_t>::max(); + + mAllocator.setUpAllocateExpectations(NO_ERROR, expectedStride); + uint32_t stride = 0; + buffer_handle_t handle; + // an overflow would cause a crash + status_t err = mAllocator.allocate(width, height, format, kTestLayerCount, kTestUsage, &handle, + &stride, 0, "GraphicBufferAllocatorTest"); + ASSERT_EQ(NO_ERROR, err); + ASSERT_EQ(expectedStride, stride); +} +} // namespace android diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp index a7c248c10..127f7eedd 100644 --- a/libs/ui/tests/GraphicBuffer_test.cpp +++ b/libs/ui/tests/GraphicBuffer_test.cpp @@ -35,6 +35,22 @@ constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN; class GraphicBufferTest : public testing::Test {}; +TEST_F(GraphicBufferTest, AllocateNoError) { + PixelFormat format = PIXEL_FORMAT_RGBA_8888; + sp<GraphicBuffer> gb(new GraphicBuffer(kTestWidth, kTestHeight, format, kTestLayerCount, + kTestUsage, std::string("test"))); + ASSERT_EQ(NO_ERROR, gb->initCheck()); +} + +TEST_F(GraphicBufferTest, AllocateBadDimensions) { + PixelFormat format = PIXEL_FORMAT_RGBA_8888; + uint32_t width, height; + width = height = std::numeric_limits<uint32_t>::max(); + sp<GraphicBuffer> gb(new GraphicBuffer(width, height, format, kTestLayerCount, kTestUsage, + std::string("test"))); + ASSERT_EQ(BAD_VALUE, gb->initCheck()); +} + TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) { std::unique_ptr<BufferHubBuffer> b1 = BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat, diff --git a/libs/ui/tests/mock/MockGrallocAllocator.cpp b/libs/ui/tests/mock/MockGrallocAllocator.cpp new file mode 100644 index 000000000..d71e25fc0 --- /dev/null +++ b/libs/ui/tests/mock/MockGrallocAllocator.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MockGrallocAllocator.h" + +namespace android { + +namespace mock { + +MockGrallocAllocator::MockGrallocAllocator() = default; +MockGrallocAllocator::~MockGrallocAllocator() = default; + +} // namespace mock +} // namespace android diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h new file mode 100644 index 000000000..22c80a463 --- /dev/null +++ b/libs/ui/tests/mock/MockGrallocAllocator.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> + +#include <ui/Gralloc.h> + +namespace android { + +class GraphicBuffer; + +namespace mock { + +class MockGrallocAllocator : public GrallocAllocator { +public: + MockGrallocAllocator(); + ~MockGrallocAllocator() override; + + MOCK_METHOD(bool, isLoaded, (), (const, override)); + MOCK_METHOD(std::string, dumpDebugInfo, (), (const, override)); + MOCK_METHOD(status_t, allocate, + (uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, + uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + buffer_handle_t* outBufferHandles), + (const, override)); +}; + +} // namespace mock +} // namespace android diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp index ac01dc8f9..ba7cacdbf 100644 --- a/opengl/libs/EGL/egl_layers.cpp +++ b/opengl/libs/EGL/egl_layers.cpp @@ -337,7 +337,7 @@ void LayerLoader::LoadLayers() { // Only enable the system search path for non-user builds std::string system_path; - if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { + if (android::GraphicsEnv::getInstance().isDebuggable()) { system_path = kSystemLayerLibraryDir; } diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index e996be685..a3bb6debe 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -61,7 +61,7 @@ namespace android { using nsecs_t = int64_t; -struct extention_map_t { +struct extension_map_t { const char* name; __eglMustCastToProperFunctionPointerType address; }; @@ -154,7 +154,7 @@ char const * const gClientExtensionString = * (keep in sync with gExtensionString above) * */ -static const extention_map_t sExtensionMap[] = { +static const extension_map_t sExtensionMap[] = { // EGL_KHR_lock_surface { "eglLockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR }, @@ -257,13 +257,14 @@ static const extention_map_t sExtensionMap[] = { !strcmp((procname), "eglAwakenProcessIMG")) // accesses protected by sExtensionMapMutex -static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap; +static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtensionMap; +static std::unordered_map<std::string, int> sGLExtensionSlotMap; -static int sGLExtentionSlot = 0; +static int sGLExtensionSlot = 0; static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER; static void(*findProcAddress(const char* name, - const extention_map_t* map, size_t n))() { + const extension_map_t* map, size_t n))() { for (uint32_t i=0 ; i<n ; i++) { if (!strcmp(name, map[i].name)) { return map[i].address; @@ -1223,7 +1224,7 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procn addr = findBuiltinWrapper(procname); if (addr) return addr; - // this protects accesses to sGLExtentionMap and sGLExtentionSlot + // this protects accesses to sGLExtensionMap, sGLExtensionSlot, and sGLExtensionSlotMap pthread_mutex_lock(&sExtensionMapMutex); /* @@ -1244,51 +1245,69 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procn */ const std::string name(procname); + auto& extensionMap = sGLExtensionMap; + auto& extensionSlotMap = sGLExtensionSlotMap; + egl_connection_t* const cnx = &gEGLImpl; + LayerLoader& layer_loader(LayerLoader::getInstance()); - auto& extentionMap = sGLExtentionMap; - auto pos = extentionMap.find(name); - addr = (pos != extentionMap.end()) ? pos->second : nullptr; - const int slot = sGLExtentionSlot; + // See if we've already looked up this extension + auto pos = extensionMap.find(name); + addr = (pos != extensionMap.end()) ? pos->second : nullptr; - ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, - "no more slots for eglGetProcAddress(\"%s\")", - procname); + if (!addr) { + // This is the first time we've looked this function up + // Ensure we have room to track it + const int slot = sGLExtensionSlot; + if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) { - egl_connection_t* const cnx = &gEGLImpl; - LayerLoader& layer_loader(LayerLoader::getInstance()); + if (cnx->dso && cnx->egl.eglGetProcAddress) { - if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { + // Extensions are independent of the bound context + addr = cnx->egl.eglGetProcAddress(procname); + if (addr) { - if (cnx->dso && cnx->egl.eglGetProcAddress) { + // purposefully track the bottom of the stack in extensionMap + extensionMap[name] = addr; - // Extensions are independent of the bound context - addr = cnx->egl.eglGetProcAddress(procname); - if (addr) { + // Apply layers + addr = layer_loader.ApplyLayers(procname, addr); - // purposefully track the bottom of the stack in extensionMap - extentionMap[name] = addr; + // Track the top most entry point return the extension forwarder + cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; + addr = gExtensionForwarders[slot]; - // Apply layers - addr = layer_loader.ApplyLayers(procname, addr); + // Remember the slot for this extension + extensionSlotMap[name] = slot; - // Track the top most entry point - cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = - cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; - addr = gExtensionForwarders[slot]; - sGLExtentionSlot++; + // Increment the global extension index + sGLExtensionSlot++; + } } + } else { + // The extension forwarder has a fixed number of slots + ALOGE("no more slots for eglGetProcAddress(\"%s\")", procname); } - } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) { + } else { + // We tracked an address, so we've seen this func before + // Look up the slot for this extension + auto slot_pos = extensionSlotMap.find(name); + int ext_slot = (slot_pos != extensionSlotMap.end()) ? slot_pos->second : -1; + if (ext_slot < 0) { + // Something has gone wrong, this should not happen + ALOGE("No extension slot found for %s", procname); + return nullptr; + } - // We've seen this func before, but we tracked the bottom, so re-apply layers - // More layers might have been enabled + // We tracked the bottom of the stack, so re-apply layers since + // more layers might have been enabled addr = layer_loader.ApplyLayers(procname, addr); - // Track the top most entry point - cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = - cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; - addr = gExtensionForwarders[slot]; + // Track the top most entry point and return the extension forwarder + cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[ext_slot] = + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr; + addr = gExtensionForwarders[ext_slot]; } pthread_mutex_unlock(&sExtensionMapMutex); diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 8dd4d1df6..576c8d8bb 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -47,7 +47,6 @@ cc_library_shared { "liblog", "libutils", "libui", - "server_configurable_flags", ], cflags: [ diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index ef1a2247e..cda0e0cdc 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -27,7 +27,6 @@ #if defined(__linux__) #include <pthread.h> #endif -#include <server_configurable_flags/get_flags.h> #include <unordered_set> #include <android/hardware/input/classifier/1.0/IInputClassifier.h> @@ -46,13 +45,6 @@ using namespace android::hardware::input; namespace android { -static constexpr bool DEBUG = false; - -// Category (=namespace) name for the input settings that are applied at boot time -static const char* INPUT_NATIVE_BOOT = "input_native_boot"; -// Feature flag name for the deep press feature -static const char* DEEP_PRESS_ENABLED = "deep_press_enabled"; - //Max number of elements to store in mEvents. static constexpr size_t MAX_EVENTS = 5; @@ -79,20 +71,6 @@ static bool isTouchEvent(const NotifyMotionArgs& args) { return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN; } -// Check if the "deep touch" feature is on. -static bool deepPressEnabled() { - std::string flag_value = server_configurable_flags::GetServerConfigurableFlag( - INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true"); - std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower); - if (flag_value == "1" || flag_value == "true") { - ALOGI("Deep press feature enabled."); - return true; - } - ALOGI("Deep press feature is not enabled."); - return false; -} - - // --- ClassifierEvent --- ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) : @@ -141,53 +119,40 @@ std::optional<int32_t> ClassifierEvent::getDeviceId() const { // --- MotionClassifier --- -MotionClassifier::MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient) : - mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) { - mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this); +MotionClassifier::MotionClassifier( + sp<android::hardware::input::classifier::V1_0::IInputClassifier> service) + : mEvents(MAX_EVENTS), mService(service) { + // Under normal operation, we do not need to reset the HAL here. But in the case where system + // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already + // have received events in the past. That means, that HAL could be in an inconsistent state + // once it receives events from the newly created MotionClassifier. + mEvents.push(ClassifierEvent::createHalResetEvent()); + + mHalThread = std::thread(&MotionClassifier::processEvents, this); #if defined(__linux__) // Set the thread name for debugging pthread_setname_np(mHalThread.native_handle(), "InputClassifier"); #endif } -/** - * This function may block for some time to initialize the HAL, so it should only be called - * from the "InputClassifier HAL" thread. - */ -bool MotionClassifier::init() { - ensureHalThread(__func__); +std::unique_ptr<MotionClassifierInterface> MotionClassifier::create( + sp<android::hardware::hidl_death_recipient> deathRecipient) { sp<android::hardware::input::classifier::V1_0::IInputClassifier> service = classifier::V1_0::IInputClassifier::getService(); if (!service) { // Not really an error, maybe the device does not have this HAL, // but somehow the feature flag is flipped ALOGI("Could not obtain InputClassifier HAL"); - return false; + return nullptr; } - sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote(); - if (recipient != nullptr) { - const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false); - if (!linked) { - ALOGE("Could not link MotionClassifier to the HAL death"); - return false; - } + const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false); + if (!linked) { + ALOGE("Could not link death recipient to the HAL death"); + return nullptr; } - - // Under normal operation, we do not need to reset the HAL here. But in the case where system - // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already - // have received events in the past. That means, that HAL could be in an inconsistent state - // once it receives events from the newly created MotionClassifier. - mEvents.push(ClassifierEvent::createHalResetEvent()); - - { - std::scoped_lock lock(mLock); - if (mService) { - ALOGE("MotionClassifier::%s should only be called once", __func__); - } - mService = service; - } - return true; + // Using 'new' to access a non-public constructor + return std::unique_ptr<MotionClassifier>(new MotionClassifier(service)); } MotionClassifier::~MotionClassifier() { @@ -195,14 +160,6 @@ MotionClassifier::~MotionClassifier() { mHalThread.join(); } -void MotionClassifier::ensureHalThread(const char* function) { - if (DEBUG) { - if (std::this_thread::get_id() != mHalThread.get_id()) { - LOG_FATAL("Function %s should only be called from InputClassifier thread", function); - } - } -} - /** * Obtain the classification from the HAL for a given MotionEvent. * Should only be called from the InputClassifier thread (mHalThread). @@ -213,23 +170,7 @@ void MotionClassifier::ensureHalThread(const char* function) { * To remove any possibility of negatively affecting the touch latency, the HAL * is called from a dedicated thread. */ -void MotionClassifier::callInputClassifierHal() { - ensureHalThread(__func__); - const bool initialized = init(); - if (!initialized) { - // MotionClassifier no longer useful. - // Deliver death notification from a separate thread - // because ~MotionClassifier may be invoked, which calls mHalThread.join() - std::thread([deathRecipient = mDeathRecipient](){ - sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote(); - if (recipient != nullptr) { - recipient->serviceDied(0 /*cookie*/, nullptr); - } - }).detach(); - return; - } - // From this point on, mService is guaranteed to be non-null. - +void MotionClassifier::processEvents() { while (true) { ClassifierEvent event = mEvents.pop(); bool halResponseOk = true; @@ -383,24 +324,41 @@ void MotionClassifier::dump(std::string& dump) { } } +// --- HalDeathRecipient -// --- InputClassifier --- +InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {} -InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) : - mListener(listener) { - // The rest of the initialization is done in onFirstRef, because we need to obtain - // an sp to 'this' in order to register for HAL death notifications +void InputClassifier::HalDeathRecipient::serviceDied( + uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) { + sp<android::hidl::base::V1_0::IBase> service = who.promote(); + if (service) { + service->unlinkToDeath(this); + } + mParent.setMotionClassifier(nullptr); } -void InputClassifier::onFirstRef() { - if (!deepPressEnabled()) { - // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work. - // When MotionClassifier is null, InputClassifier will forward all events - // to the next InputListener, unmodified. - return; +// --- InputClassifier --- + +InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) + : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {} + +void InputClassifier::setMotionClassifierEnabled(bool enabled) { + if (enabled) { + ALOGI("Enabling motion classifier"); + if (mInitializeMotionClassifierThread.joinable()) { + mInitializeMotionClassifierThread.join(); + } + mInitializeMotionClassifierThread = std::thread( + [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); }); +#if defined(__linux__) + // Set the thread name for debugging + pthread_setname_np(mInitializeMotionClassifierThread.native_handle(), + "Create MotionClassifier"); +#endif + } else { + ALOGI("Disabling motion classifier"); + setMotionClassifier(nullptr); } - std::scoped_lock lock(mLock); - mMotionClassifier = std::make_unique<MotionClassifier>(this); } void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { @@ -441,15 +399,10 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mListener->notifyDeviceReset(args); } -void InputClassifier::serviceDied(uint64_t /*cookie*/, - const wp<android::hidl::base::V1_0::IBase>& who) { +void InputClassifier::setMotionClassifier( + std::unique_ptr<MotionClassifierInterface> motionClassifier) { std::scoped_lock lock(mLock); - ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null"); - mMotionClassifier = nullptr; - sp<android::hidl::base::V1_0::IBase> service = who.promote(); - if (service) { - service->unlinkToDeath(this); - } + mMotionClassifier = std::move(motionClassifier); } void InputClassifier::dump(std::string& dump) { @@ -465,4 +418,10 @@ void InputClassifier::dump(std::string& dump) { dump += "\n"; } +InputClassifier::~InputClassifier() { + if (mInitializeMotionClassifierThread.joinable()) { + mInitializeMotionClassifierThread.join(); + } +} + } // namespace android diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index 47e20dbf7..9ac8e2db2 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -19,8 +19,8 @@ #include <android-base/thread_annotations.h> #include <utils/RefBase.h> -#include <unordered_map> #include <thread> +#include <unordered_map> #include "BlockingQueue.h" #include "InputListener.h" @@ -90,6 +90,7 @@ public: */ class InputClassifierInterface : public virtual RefBase, public InputListenerInterface { public: + virtual void setMotionClassifierEnabled(bool enabled) = 0; /** * Dump the state of the input classifier. * This method may be called on any thread (usually by the input manager). @@ -113,23 +114,23 @@ protected: */ class MotionClassifier final : public MotionClassifierInterface { public: - /** - * The deathRecipient will be subscribed to the HAL death. If the death recipient - * owns MotionClassifier and receives HAL death, it should delete its copy of it. - * The callback serviceDied will also be sent if the MotionClassifier itself fails - * to initialize. If the MotionClassifier fails to initialize, it is not useful, and - * should be deleted. - * If no death recipient is supplied, then the registration step will be skipped, so there will - * be no listeners registered for the HAL death. This is useful for testing - * MotionClassifier in isolation. + /* + * Create an instance of MotionClassifier. + * The death recipient, if provided, will be subscribed to the HAL death. + * The death recipient could be used to destroy MotionClassifier. + * + * This function should be called asynchronously, because getService takes a long time. */ - explicit MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient = nullptr); + static std::unique_ptr<MotionClassifierInterface> create( + sp<android::hardware::hidl_death_recipient> deathRecipient); + ~MotionClassifier(); /** * Classifies events asynchronously; that is, it doesn't block events on a classification, - * but instead sends them over to the classifier HAL and after a classification is - * determined, it then marks the next event it sees in the stream with it. + * but instead sends them over to the classifier HAL. After a classification of a specific + * event is determined, MotionClassifier then marks the next event in the stream with this + * classification. * * Therefore, it is acceptable to have the classifications be delayed by 1-2 events * in a particular gesture. @@ -141,15 +142,9 @@ public: virtual void dump(std::string& dump) override; private: - /** - * Initialize MotionClassifier. - * Return true if initializaion is successful. - */ - bool init(); - /** - * Entity that will be notified of the HAL death (most likely InputClassifier). - */ - wp<android::hardware::hidl_death_recipient> mDeathRecipient; + friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation + explicit MotionClassifier( + sp<android::hardware::input::classifier::V1_0::IInputClassifier> service); // The events that need to be sent to the HAL. BlockingQueue<ClassifierEvent> mEvents; @@ -164,14 +159,9 @@ private: */ std::thread mHalThread; /** - * Print an error message if the caller is not on the InputClassifier thread. - * Caller must supply the name of the calling function as __func__ + * Process events and call the InputClassifier HAL */ - void ensureHalThread(const char* function); - /** - * Call the InputClassifier HAL - */ - void callInputClassifierHal(); + void processEvents(); /** * Access to the InputClassifier HAL. May be null if init() hasn't completed yet. * When init() successfully completes, mService is guaranteed to remain non-null and to not @@ -223,19 +213,15 @@ private: const char* getServiceStatus() REQUIRES(mLock); }; - /** * Implementation of the InputClassifierInterface. * Represents a separate stage of input processing. All of the input events go through this stage. * Acts as a passthrough for all input events except for motion events. * The events of motion type are sent to MotionClassifier. */ -class InputClassifier : public InputClassifierInterface, - public android::hardware::hidl_death_recipient { +class InputClassifier : public InputClassifierInterface { public: explicit InputClassifier(const sp<InputListenerInterface>& listener); - // Some of the constructor logic is finished in onFirstRef - virtual void onFirstRef() override; virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; @@ -243,17 +229,47 @@ public: virtual void notifySwitch(const NotifySwitchArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; - virtual void serviceDied(uint64_t cookie, - const wp<android::hidl::base::V1_0::IBase>& who) override; - virtual void dump(std::string& dump) override; + ~InputClassifier(); + + // Called from InputManager + virtual void setMotionClassifierEnabled(bool enabled) override; + private: // Protect access to mMotionClassifier, since it may become null via a hidl callback std::mutex mLock; - std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock); // The next stage to pass input events to sp<InputListenerInterface> mListener; + + std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock); + std::thread mInitializeMotionClassifierThread; + /** + * Set the value of mMotionClassifier. + * This is called from 2 different threads: + * 1) mInitializeMotionClassifierThread, when we have constructed a MotionClassifier + * 2) A binder thread of the HalDeathRecipient, which is created when HAL dies. This would cause + * mMotionClassifier to become nullptr. + */ + void setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier); + + /** + * The deathRecipient will call setMotionClassifier(null) when the HAL dies. + */ + class HalDeathRecipient : public android::hardware::hidl_death_recipient { + public: + explicit HalDeathRecipient(InputClassifier& parent); + virtual void serviceDied(uint64_t cookie, + const wp<android::hidl::base::V1_0::IBase>& who) override; + + private: + InputClassifier& mParent; + }; + // We retain a reference to death recipient, because the death recipient will be calling + // ~MotionClassifier if the HAL dies. + // If we don't retain a reference, and MotionClassifier is the only owner of the death + // recipient, the serviceDied call will cause death recipient to call its own destructor. + sp<HalDeathRecipient> mHalDeathRecipient; }; } // namespace android diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 3996cca64..52e908066 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -117,10 +117,6 @@ void InputManager::setInputWindows(const std::vector<InputWindowInfo>& infos, } } -void InputManager::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) { - mDispatcher->transferTouchFocus(fromToken, toToken); -} - // Used by tests only. void InputManager::registerInputChannel(const sp<InputChannel>& channel) { IPCThreadState* ipc = IPCThreadState::self(); @@ -137,4 +133,8 @@ void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) { mDispatcher->unregisterInputChannel(channel); } +void InputManager::setMotionClassifierEnabled(bool enabled) { + mClassifier->setMotionClassifierEnabled(enabled); +} + } // namespace android diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index e568df54d..c75611f08 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -96,11 +96,12 @@ public: virtual void setInputWindows(const std::vector<InputWindowInfo>& handles, const sp<ISetInputWindowsListener>& setInputWindowsListener); - virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken); virtual void registerInputChannel(const sp<InputChannel>& channel); virtual void unregisterInputChannel(const sp<InputChannel>& channel); + void setMotionClassifierEnabled(bool enabled); + private: sp<InputReaderInterface> mReader; sp<InputReaderThread> mReaderThread; diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h index d8b352cbc..973b4f92f 100644 --- a/services/inputflinger/host/InputFlinger.h +++ b/services/inputflinger/host/InputFlinger.h @@ -42,7 +42,6 @@ public: virtual status_t dump(int fd, const Vector<String16>& args); void setInputWindows(const std::vector<InputWindowInfo>&, const sp<ISetInputWindowsListener>&) {} - void transferTouchFocus(const sp<IBinder>&, const sp<IBinder>&) {} void registerInputChannel(const sp<InputChannel>&) {} void unregisterInputChannel(const sp<InputChannel>&) {} diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index 7cc17a221..4cddec50d 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -22,6 +22,9 @@ #include <android/hardware/input/classifier/1.0/IInputClassifier.h> using namespace android::hardware::input; +using android::hardware::Return; +using android::hardware::Void; +using android::hardware::input::common::V1_0::Classification; namespace android { @@ -129,6 +132,49 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) { ASSERT_EQ(args, outArgs); } +TEST_F(InputClassifierTest, SetMotionClassifier_Enabled) { + mClassifier->setMotionClassifierEnabled(true); +} + +TEST_F(InputClassifierTest, SetMotionClassifier_Disabled) { + mClassifier->setMotionClassifierEnabled(false); +} + +/** + * Try to break it by calling setMotionClassifierEnabled multiple times. + */ +TEST_F(InputClassifierTest, SetMotionClassifier_Multiple) { + mClassifier->setMotionClassifierEnabled(true); + mClassifier->setMotionClassifierEnabled(true); + mClassifier->setMotionClassifierEnabled(true); + mClassifier->setMotionClassifierEnabled(false); + mClassifier->setMotionClassifierEnabled(false); + mClassifier->setMotionClassifierEnabled(true); + mClassifier->setMotionClassifierEnabled(true); + mClassifier->setMotionClassifierEnabled(true); +} + +/** + * A minimal implementation of IInputClassifier. + */ +struct TestHal : public android::hardware::input::classifier::V1_0::IInputClassifier { + Return<Classification> classify( + const android::hardware::input::common::V1_0::MotionEvent& event) override { + return Classification::NONE; + }; + Return<void> reset() override { return Void(); }; + Return<void> resetDevice(int32_t deviceId) override { return Void(); }; +}; + +/** + * An entity that will be subscribed to the HAL death. + */ +class TestDeathRecipient : public android::hardware::hidl_death_recipient { +public: + virtual void serviceDied(uint64_t cookie, + const wp<android::hidl::base::V1_0::IBase>& who) override{}; +}; + // --- MotionClassifierTest --- class MotionClassifierTest : public testing::Test { @@ -136,7 +182,14 @@ protected: std::unique_ptr<MotionClassifierInterface> mMotionClassifier; virtual void SetUp() override { - mMotionClassifier = std::make_unique<MotionClassifier>(); + mMotionClassifier = MotionClassifier::create(new TestDeathRecipient()); + if (mMotionClassifier == nullptr) { + // If the device running this test does not have IInputClassifier service, + // use the test HAL instead. + // Using 'new' to access non-public constructor + mMotionClassifier = + std::unique_ptr<MotionClassifier>(new MotionClassifier(new TestHal())); + } } }; diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index c11b88eac..090a0ce47 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -208,12 +208,6 @@ void SensorService::onFirstRef() { registerSensor(new RotationVectorSensor(), !needRotationVector, true); registerSensor(new OrientationSensor(), !needRotationVector, true); - bool needLinearAcceleration = - (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0; - - registerSensor(new LinearAccelerationSensor(list, count), - !needLinearAcceleration, true); - // virtual debugging sensors are not for user registerSensor( new CorrectedGyroSensor(list, count), true, true); registerSensor( new GyroDriftSensor(), true, true); @@ -223,6 +217,11 @@ void SensorService::onFirstRef() { bool needGravitySensor = (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) != 0; registerSensor(new GravitySensor(list, count), !needGravitySensor, true); + bool needLinearAcceleration = + (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0; + registerSensor(new LinearAccelerationSensor(list, count), + !needLinearAcceleration, true); + bool needGameRotationVector = (virtualSensorsNeeds & (1<<SENSOR_TYPE_GAME_ROTATION_VECTOR)) != 0; registerSensor(new GameRotationVectorSensor(), !needGameRotationVector, true); @@ -299,14 +298,10 @@ void SensorService::onFirstRef() { } void SensorService::setSensorAccess(uid_t uid, bool hasAccess) { - SortedVector< sp<SensorEventConnection> > activeConnections; - populateActiveConnections(&activeConnections); - { - Mutex::Autolock _l(mLock); - for (size_t i = 0 ; i < activeConnections.size(); i++) { - if (activeConnections[i] != nullptr && activeConnections[i]->getUid() == uid) { - activeConnections[i]->setSensorAccess(hasAccess); - } + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); + for (const sp<SensorEventConnection>& conn : connLock.getActiveConnections()) { + if (conn->getUid() == uid) { + conn->setSensorAccess(hasAccess); } } } @@ -360,7 +355,7 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { if (args.size() > 2) { return INVALID_OPERATION; } - Mutex::Autolock _l(mLock); + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); SensorDevice& dev(SensorDevice::getInstance()); if (args.size() == 2 && args[0] == String16("restrict")) { // If already in restricted mode. Ignore. @@ -374,7 +369,7 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { mCurrentOperatingMode = RESTRICTED; // temporarily stop all sensor direct report and disable sensors - disableAllSensorsLocked(); + disableAllSensorsLocked(&connLock); mWhiteListedPackage.setTo(String8(args[1])); return status_t(NO_ERROR); } else if (args.size() == 1 && args[0] == String16("enable")) { @@ -382,7 +377,7 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { if (mCurrentOperatingMode == RESTRICTED) { mCurrentOperatingMode = NORMAL; // enable sensors and recover all sensor direct report - enableAllSensorsLocked(); + enableAllSensorsLocked(&connLock); } if (mCurrentOperatingMode == DATA_INJECTION) { resetToNormalModeLocked(); @@ -473,22 +468,18 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { result.appendFormat("Sensor Privacy: %s\n", mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled"); - result.appendFormat("%zd active connections\n", mActiveConnections.size()); - for (size_t i=0 ; i < mActiveConnections.size() ; i++) { - sp<SensorEventConnection> connection(mActiveConnections[i].promote()); - if (connection != nullptr) { - result.appendFormat("Connection Number: %zu \n", i); - connection->dump(result); - } + const auto& activeConnections = connLock.getActiveConnections(); + result.appendFormat("%zd active connections\n", activeConnections.size()); + for (size_t i=0 ; i < activeConnections.size() ; i++) { + result.appendFormat("Connection Number: %zu \n", i); + activeConnections[i]->dump(result); } - result.appendFormat("%zd direct connections\n", mDirectConnections.size()); - for (size_t i = 0 ; i < mDirectConnections.size() ; i++) { - sp<SensorDirectConnection> connection(mDirectConnections[i].promote()); - if (connection != nullptr) { - result.appendFormat("Direct connection %zu:\n", i); - connection->dump(result); - } + const auto& directConnections = connLock.getDirectConnections(); + result.appendFormat("%zd direct connections\n", directConnections.size()); + for (size_t i = 0 ; i < directConnections.size() ; i++) { + result.appendFormat("Direct connection %zu:\n", i); + directConnections[i]->dump(result); } result.appendFormat("Previous Registrations:\n"); @@ -515,17 +506,14 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { } void SensorService::disableAllSensors() { - Mutex::Autolock _l(mLock); - disableAllSensorsLocked(); + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); + disableAllSensorsLocked(&connLock); } -void SensorService::disableAllSensorsLocked() { +void SensorService::disableAllSensorsLocked(ConnectionSafeAutolock* connLock) { SensorDevice& dev(SensorDevice::getInstance()); - for (auto &i : mDirectConnections) { - sp<SensorDirectConnection> connection(i.promote()); - if (connection != nullptr) { - connection->stopAll(true /* backupRecord */); - } + for (const sp<SensorDirectConnection>& connection : connLock->getDirectConnections()) { + connection->stopAll(true /* backupRecord */); } dev.disableAllSensors(); // Clear all pending flush connections for all active sensors. If one of the active @@ -537,11 +525,11 @@ void SensorService::disableAllSensorsLocked() { } void SensorService::enableAllSensors() { - Mutex::Autolock _l(mLock); - enableAllSensorsLocked(); + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); + enableAllSensorsLocked(&connLock); } -void SensorService::enableAllSensorsLocked() { +void SensorService::enableAllSensorsLocked(ConnectionSafeAutolock* connLock) { // sensors should only be enabled if the operating state is not restricted and sensor // privacy is not enabled. if (mCurrentOperatingMode == RESTRICTED || mSensorPrivacyPolicy->isSensorPrivacyEnabled()) { @@ -552,14 +540,12 @@ void SensorService::enableAllSensorsLocked() { } SensorDevice& dev(SensorDevice::getInstance()); dev.enableAllSensors(); - for (auto &i : mDirectConnections) { - sp<SensorDirectConnection> connection(i.promote()); - if (connection != nullptr) { - connection->recoverAll(); - } + for (const sp<SensorDirectConnection>& connection : connLock->getDirectConnections()) { + connection->recoverAll(); } } + // NOTE: This is a remote API - make sure all args are validated status_t SensorService::shellCommand(int in, int out, int err, Vector<String16>& args) { if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) { @@ -734,17 +720,8 @@ bool SensorService::threadLoop() { for (int i = 0; i < count; i++) { mSensorEventBuffer[i].flags = 0; } + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); - // Make a copy of the connection vector as some connections may be removed during the course - // of this loop (especially when one-shot sensor events are present in the sensor_event - // buffer). Promote all connections to StrongPointers before the lock is acquired. If the - // destructor of the sp gets called when the lock is acquired, it may result in a deadlock - // as ~SensorEventConnection() needs to acquire mLock again for cleanup. So copy all the - // strongPointers to a vector before the lock is acquired. - SortedVector< sp<SensorEventConnection> > activeConnections; - populateActiveConnections(&activeConnections); - - Mutex::Autolock _l(mLock); // Poll has returned. Hold a wakelock if one of the events is from a wake up sensor. The // rest of this loop is under a critical section protected by mLock. Acquiring a wakeLock, // sending events to clients (incrementing SensorEventConnection::mWakeLockRefCount) should @@ -818,6 +795,10 @@ bool SensorService::threadLoop() { } } + // Cache the list of active connections, since we use it in multiple places below but won't + // modify it here + const std::vector<sp<SensorEventConnection>> activeConnections = connLock.getActiveConnections(); + for (int i = 0; i < count; ++i) { // Map flush_complete_events in the buffer to SensorEventConnections which called flush // on the hardware sensor. mapFlushEventsToConnections[i] will be the @@ -869,11 +850,8 @@ bool SensorService::threadLoop() { ALOGE("Dynamic sensor release error."); } - size_t numConnections = activeConnections.size(); - for (size_t i=0 ; i < numConnections; ++i) { - if (activeConnections[i] != nullptr) { - activeConnections[i]->removeSensor(handle); - } + for (const sp<SensorEventConnection>& connection : activeConnections) { + connection->removeSensor(handle); } } } @@ -882,18 +860,14 @@ bool SensorService::threadLoop() { // Send our events to clients. Check the state of wake lock for each client and release the // lock if none of the clients need it. bool needsWakeLock = false; - size_t numConnections = activeConnections.size(); - for (size_t i=0 ; i < numConnections; ++i) { - if (activeConnections[i] != nullptr) { - activeConnections[i]->sendEvents(mSensorEventBuffer, count, mSensorEventScratch, - mMapFlushEventsToConnections); - needsWakeLock |= activeConnections[i]->needsWakeLock(); - // If the connection has one-shot sensors, it may be cleaned up after first trigger. - // Early check for one-shot sensors. - if (activeConnections[i]->hasOneShotSensors()) { - cleanupAutoDisabledSensorLocked(activeConnections[i], mSensorEventBuffer, - count); - } + for (const sp<SensorEventConnection>& connection : activeConnections) { + connection->sendEvents(mSensorEventBuffer, count, mSensorEventScratch, + mMapFlushEventsToConnections); + needsWakeLock |= connection->needsWakeLock(); + // If the connection has one-shot sensors, it may be cleaned up after first trigger. + // Early check for one-shot sensors. + if (connection->hasOneShotSensors()) { + cleanupAutoDisabledSensorLocked(connection, mSensorEventBuffer, count); } } @@ -912,17 +886,11 @@ sp<Looper> SensorService::getLooper() const { } void SensorService::resetAllWakeLockRefCounts() { - SortedVector< sp<SensorEventConnection> > activeConnections; - populateActiveConnections(&activeConnections); - { - Mutex::Autolock _l(mLock); - for (size_t i=0 ; i < activeConnections.size(); ++i) { - if (activeConnections[i] != nullptr) { - activeConnections[i]->resetWakeLockRefCount(); - } - } - setWakeLockAcquiredLocked(false); + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); + for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) { + connection->resetWakeLockRefCount(); } + setWakeLockAcquiredLocked(false); } void SensorService::setWakeLockAcquiredLocked(bool acquire) { @@ -1144,9 +1112,7 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri sp<SensorEventConnection> result(new SensorEventConnection(this, uid, connPackageName, requestedMode == DATA_INJECTION, connOpPackageName, hasSensorAccess)); if (requestedMode == DATA_INJECTION) { - if (mActiveConnections.indexOf(result) < 0) { - mActiveConnections.add(result); - } + mConnectionHolder.addEventConnectionIfNotPresent(result); // Add the associated file descriptor to the Looper for polling whenever there is data to // be injected. result->updateLooperRegistration(mLooper); @@ -1162,7 +1128,7 @@ int SensorService::isDataInjectionEnabled() { sp<ISensorEventConnection> SensorService::createSensorDirectConnection( const String16& opPackageName, uint32_t size, int32_t type, int32_t format, const native_handle *resource) { - Mutex::Autolock _l(mLock); + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); // No new direct connections are allowed when sensor privacy is enabled if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) { @@ -1190,9 +1156,8 @@ sp<ISensorEventConnection> SensorService::createSensorDirectConnection( } // check for duplication - for (auto &i : mDirectConnections) { - sp<SensorDirectConnection> connection(i.promote()); - if (connection != nullptr && connection->isEquivalent(&mem)) { + for (const sp<SensorDirectConnection>& connection : connLock.getDirectConnections()) { + if (connection->isEquivalent(&mem)) { ALOGE("Duplicate create channel request for the same share memory"); return nullptr; } @@ -1229,7 +1194,7 @@ sp<ISensorEventConnection> SensorService::createSensorDirectConnection( return nullptr; } - SensorDirectConnection* conn = nullptr; + sp<SensorDirectConnection> conn; SensorDevice& dev(SensorDevice::getInstance()); int channelHandle = dev.registerDirectChannel(&mem); @@ -1246,7 +1211,7 @@ sp<ISensorEventConnection> SensorService::createSensorDirectConnection( } else { // add to list of direct connections // sensor service should never hold pointer or sp of SensorDirectConnection object. - mDirectConnections.add(wp<SensorDirectConnection>(conn)); + mConnectionHolder.addDirectConnection(conn); } return conn; } @@ -1358,7 +1323,7 @@ status_t SensorService::resetToNormalModeLocked() { } void SensorService::cleanupConnection(SensorEventConnection* c) { - Mutex::Autolock _l(mLock); + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); const wp<SensorEventConnection> connection(c); size_t size = mActiveSensors.size(); ALOGD_IF(DEBUG_CONNECTIONS, "%zu active sensors", size); @@ -1391,10 +1356,10 @@ void SensorService::cleanupConnection(SensorEventConnection* c) { } } c->updateLooperRegistration(mLooper); - mActiveConnections.remove(connection); + mConnectionHolder.removeEventConnection(connection); BatteryService::cleanup(c->getUid()); if (c->needsWakeLock()) { - checkWakeLockStateLocked(); + checkWakeLockStateLocked(&connLock); } { @@ -1414,7 +1379,7 @@ void SensorService::cleanupConnection(SensorDirectConnection* c) { SensorDevice& dev(SensorDevice::getInstance()); dev.unregisterDirectChannel(c->getHalChannelHandle()); - mDirectConnections.remove(c); + mConnectionHolder.removeDirectConnection(c); } sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const { @@ -1433,7 +1398,7 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, return BAD_VALUE; } - Mutex::Autolock _l(mLock); + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); if (mCurrentOperatingMode != NORMAL && !isWhiteListedPackage(connection->getPackageName())) { return INVALID_OPERATION; @@ -1484,7 +1449,7 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, } connection->sendEvents(&event, 1, nullptr); if (!connection->needsWakeLock() && mWakeLockAcquired) { - checkWakeLockStateLocked(); + checkWakeLockStateLocked(&connLock); } } } @@ -1497,9 +1462,7 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, BatteryService::enableSensor(connection->getUid(), handle); // the sensor was added (which means it wasn't already there) // so, see if this connection becomes active - if (mActiveConnections.indexOf(connection) < 0) { - mActiveConnections.add(connection); - } + mConnectionHolder.addEventConnectionIfNotPresent(connection); } else { ALOGW("sensor %08x already enabled in connection %p (ignoring)", handle, connection.get()); @@ -1603,7 +1566,7 @@ status_t SensorService::cleanupWithoutDisableLocked( } if (connection->hasAnySensor() == false) { connection->updateLooperRegistration(mLooper); - mActiveConnections.remove(connection); + mConnectionHolder.removeEventConnection(connection); } // see if this sensor becomes inactive if (rec->removeConnection(connection)) { @@ -1762,22 +1725,19 @@ int SensorService::getTargetSdkVersion(const String16& opPackageName) { } void SensorService::checkWakeLockState() { - Mutex::Autolock _l(mLock); - checkWakeLockStateLocked(); + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); + checkWakeLockStateLocked(&connLock); } -void SensorService::checkWakeLockStateLocked() { +void SensorService::checkWakeLockStateLocked(ConnectionSafeAutolock* connLock) { if (!mWakeLockAcquired) { return; } bool releaseLock = true; - for (size_t i=0 ; i<mActiveConnections.size() ; i++) { - sp<SensorEventConnection> connection(mActiveConnections[i].promote()); - if (connection != nullptr) { - if (connection->needsWakeLock()) { - releaseLock = false; - break; - } + for (const sp<SensorEventConnection>& connection : connLock->getActiveConnections()) { + if (connection->needsWakeLock()) { + releaseLock = false; + break; } } if (releaseLock) { @@ -1793,17 +1753,6 @@ void SensorService::sendEventsFromCache(const sp<SensorEventConnection>& connect } } -void SensorService::populateActiveConnections( - SortedVector< sp<SensorEventConnection> >* activeConnections) { - Mutex::Autolock _l(mLock); - for (size_t i=0 ; i < mActiveConnections.size(); ++i) { - sp<SensorEventConnection> connection(mActiveConnections[i].promote()); - if (connection != nullptr) { - activeConnections->add(connection); - } - } -} - bool SensorService::isWhiteListedPackage(const String8& packageName) { return (packageName.contains(mWhiteListedPackage.string())); } @@ -1938,4 +1887,62 @@ binder::Status SensorService::SensorPrivacyPolicy::onSensorPrivacyChanged(bool e } return binder::Status::ok(); } -}; // namespace android + +SensorService::ConnectionSafeAutolock::ConnectionSafeAutolock( + SensorService::SensorConnectionHolder& holder, Mutex& mutex) + : mConnectionHolder(holder), mAutolock(mutex) {} + +template<typename ConnectionType> +const std::vector<sp<ConnectionType>>& SensorService::ConnectionSafeAutolock::getConnectionsHelper( + const SortedVector<wp<ConnectionType>>& connectionList, + std::vector<std::vector<sp<ConnectionType>>>* referenceHolder) { + referenceHolder->emplace_back(); + std::vector<sp<ConnectionType>>& connections = referenceHolder->back(); + for (const wp<ConnectionType>& weakConnection : connectionList){ + sp<ConnectionType> connection = weakConnection.promote(); + if (connection != nullptr) { + connections.push_back(std::move(connection)); + } + } + return connections; +} + +const std::vector<sp<SensorService::SensorEventConnection>>& + SensorService::ConnectionSafeAutolock::getActiveConnections() { + return getConnectionsHelper(mConnectionHolder.mActiveConnections, + &mReferencedActiveConnections); +} + +const std::vector<sp<SensorService::SensorDirectConnection>>& + SensorService::ConnectionSafeAutolock::getDirectConnections() { + return getConnectionsHelper(mConnectionHolder.mDirectConnections, + &mReferencedDirectConnections); +} + +void SensorService::SensorConnectionHolder::addEventConnectionIfNotPresent( + const sp<SensorService::SensorEventConnection>& connection) { + if (mActiveConnections.indexOf(connection) < 0) { + mActiveConnections.add(connection); + } +} + +void SensorService::SensorConnectionHolder::removeEventConnection( + const wp<SensorService::SensorEventConnection>& connection) { + mActiveConnections.remove(connection); +} + +void SensorService::SensorConnectionHolder::addDirectConnection( + const sp<SensorService::SensorDirectConnection>& connection) { + mDirectConnections.add(connection); +} + +void SensorService::SensorConnectionHolder::removeDirectConnection( + const wp<SensorService::SensorDirectConnection>& connection) { + mDirectConnections.remove(connection); +} + +SensorService::ConnectionSafeAutolock SensorService::SensorConnectionHolder::lock(Mutex& mutex) { + return ConnectionSafeAutolock(*this, mutex); +} + +} // namespace android diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index e6ec96dd0..060b5eba7 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -20,6 +20,7 @@ #include "SensorList.h" #include "RecentEventLogger.h" +#include <android-base/macros.h> #include <binder/AppOpsManager.h> #include <binder/BinderService.h> #include <binder/IUidObserver.h> @@ -42,6 +43,7 @@ #include <sys/types.h> #include <unordered_map> #include <unordered_set> +#include <vector> #if __clang__ // Clang warns about SensorEventConnection::dump hiding BBinder::dump. The cause isn't fixable @@ -95,10 +97,67 @@ private: friend class BinderService<SensorService>; // nested class/struct for internal use - class SensorRecord; + class ConnectionSafeAutolock; + class SensorConnectionHolder; class SensorEventAckReceiver; + class SensorRecord; class SensorRegistrationInfo; + // Promoting a SensorEventConnection or SensorDirectConnection from wp to sp must be done with + // mLock held, but destroying that sp must be done unlocked to avoid a race condition that + // causes a deadlock (remote dies while we hold a local sp, then our decStrong() call invokes + // the dtor -> cleanupConnection() tries to re-lock the mutex). This class ensures safe usage + // by wrapping a Mutex::Autolock on SensorService's mLock, plus vectors that hold promoted sp<> + // references until the lock is released, when they are safely destroyed. + // All read accesses to the connection lists in mConnectionHolder must be done via this class. + class ConnectionSafeAutolock final { + public: + // Returns a list of non-null promoted connection references + const std::vector<sp<SensorEventConnection>>& getActiveConnections(); + const std::vector<sp<SensorDirectConnection>>& getDirectConnections(); + + private: + // Constructed via SensorConnectionHolder::lock() + friend class SensorConnectionHolder; + explicit ConnectionSafeAutolock(SensorConnectionHolder& holder, Mutex& mutex); + DISALLOW_IMPLICIT_CONSTRUCTORS(ConnectionSafeAutolock); + + // NOTE: Order of these members is important, as the destructor for non-static members + // get invoked in the reverse order of their declaration. Here we are relying on the + // Autolock to be destroyed *before* the vectors, so the sp<> objects are destroyed without + // the lock held, which avoids the deadlock. + SensorConnectionHolder& mConnectionHolder; + std::vector<std::vector<sp<SensorEventConnection>>> mReferencedActiveConnections; + std::vector<std::vector<sp<SensorDirectConnection>>> mReferencedDirectConnections; + Mutex::Autolock mAutolock; + + template<typename ConnectionType> + const std::vector<sp<ConnectionType>>& getConnectionsHelper( + const SortedVector<wp<ConnectionType>>& connectionList, + std::vector<std::vector<sp<ConnectionType>>>* referenceHolder); + }; + + // Encapsulates the collection of active SensorEventConection and SensorDirectConnection + // references. Write access is done through this class with mLock held, but all read access + // must be routed through ConnectionSafeAutolock. + class SensorConnectionHolder { + public: + void addEventConnectionIfNotPresent(const sp<SensorEventConnection>& connection); + void removeEventConnection(const wp<SensorEventConnection>& connection); + + void addDirectConnection(const sp<SensorDirectConnection>& connection); + void removeDirectConnection(const wp<SensorDirectConnection>& connection); + + // Pass in the mutex that protects this connection holder; acquires the lock and returns an + // object that can be used to safely read the lists of connections + ConnectionSafeAutolock lock(Mutex& mutex); + + private: + friend class ConnectionSafeAutolock; + SortedVector< wp<SensorEventConnection> > mActiveConnections; + SortedVector< wp<SensorDirectConnection> > mDirectConnections; + }; + // If accessing a sensor we need to make sure the UID has access to it. If // the app UID is idle then it cannot access sensors and gets no trigger // events, no on-change events, flush event behavior does not change, and @@ -250,7 +309,7 @@ private: // method checks whether all the events from these wake up sensors have been delivered to the // corresponding applications, if yes the wakelock is released. void checkWakeLockState(); - void checkWakeLockStateLocked(); + void checkWakeLockStateLocked(ConnectionSafeAutolock* connLock); bool isWakeLockAcquired(); bool isWakeUpSensorEvent(const sensors_event_t& event) const; @@ -268,10 +327,6 @@ private: // Send events from the event cache for this particular connection. void sendEventsFromCache(const sp<SensorEventConnection>& connection); - // Promote all weak referecences in mActiveConnections vector to strong references and add them - // to the output vector. - void populateActiveConnections( SortedVector< sp<SensorEventConnection> >* activeConnections); - // If SensorService is operating in RESTRICTED mode, only select whitelisted packages are // allowed to register for or call flush on sensors. Typically only cts test packages are // allowed. @@ -306,10 +361,10 @@ private: // temporarily stops all active direct connections and disables all sensors void disableAllSensors(); - void disableAllSensorsLocked(); + void disableAllSensorsLocked(ConnectionSafeAutolock* connLock); // restarts the previously stopped direct connections and enables all sensors void enableAllSensors(); - void enableAllSensorsLocked(); + void enableAllSensorsLocked(ConnectionSafeAutolock* connLock); static uint8_t sHmacGlobalKey[128]; static bool sHmacGlobalKeyIsValid; @@ -327,12 +382,13 @@ private: mutable Mutex mLock; DefaultKeyedVector<int, SensorRecord*> mActiveSensors; std::unordered_set<int> mActiveVirtualSensors; - SortedVector< wp<SensorEventConnection> > mActiveConnections; + SensorConnectionHolder mConnectionHolder; bool mWakeLockAcquired; sensors_event_t *mSensorEventBuffer, *mSensorEventScratch; + // WARNING: these SensorEventConnection instances must not be promoted to sp, except via + // modification to add support for them in ConnectionSafeAutolock wp<const SensorEventConnection> * mMapFlushEventsToConnections; std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent; - SortedVector< wp<SensorDirectConnection> > mDirectConnections; Mode mCurrentOperatingMode; // This packagaName is set when SensorService is in RESTRICTED or DATA_INJECTION mode. Only diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 4cd0a1386..501d17640 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -149,9 +149,10 @@ filegroup { "Scheduler/LayerHistory.cpp", "Scheduler/LayerInfo.cpp", "Scheduler/MessageQueue.cpp", + "Scheduler/PhaseOffsets.cpp", "Scheduler/Scheduler.cpp", "Scheduler/SchedulerUtils.cpp", - "Scheduler/PhaseOffsets.cpp", + "Scheduler/VSyncModulator.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", "SurfaceInterceptor.cpp", diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index b679380b7..46a62eda9 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -48,7 +48,7 @@ namespace android { class BufferLayer : public Layer { public: explicit BufferLayer(const LayerCreationArgs& args); - ~BufferLayer() override; + virtual ~BufferLayer() override; // ----------------------------------------------------------------------- // Overriden from Layer diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp index 6709fb4b4..414814a6f 100644 --- a/services/surfaceflinger/BufferLayerConsumer.cpp +++ b/services/surfaceflinger/BufferLayerConsumer.cpp @@ -369,6 +369,15 @@ const Region& BufferLayerConsumer::getSurfaceDamage() const { return mCurrentSurfaceDamage; } +void BufferLayerConsumer::mergeSurfaceDamage(const Region& damage) { + if (damage.bounds() == Rect::INVALID_RECT || + mCurrentSurfaceDamage.bounds() == Rect::INVALID_RECT) { + mCurrentSurfaceDamage = Region::INVALID_REGION; + } else { + mCurrentSurfaceDamage |= damage; + } +} + int BufferLayerConsumer::getCurrentApi() const { Mutex::Autolock lock(mMutex); return mCurrentApi; @@ -485,7 +494,6 @@ void BufferLayerConsumer::onBufferAvailable(const BufferItem& item) { if (oldImage == nullptr || oldImage->graphicBuffer() == nullptr || oldImage->graphicBuffer()->getId() != item.mGraphicBuffer->getId()) { mImages[item.mSlot] = std::make_shared<Image>(item.mGraphicBuffer, mRE); - mRE.cacheExternalTextureBuffer(item.mGraphicBuffer); } } } @@ -522,6 +530,12 @@ void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const ConsumerBase::dumpLocked(result, prefix); } +BufferLayerConsumer::Image::Image(const sp<GraphicBuffer>& graphicBuffer, + renderengine::RenderEngine& engine) + : mGraphicBuffer(graphicBuffer), mRE(engine) { + mRE.cacheExternalTextureBuffer(mGraphicBuffer); +} + BufferLayerConsumer::Image::~Image() { if (mGraphicBuffer != nullptr) { ALOGV("Destroying buffer: %" PRId64, mGraphicBuffer->getId()); diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h index e3f6100c3..617b1c232 100644 --- a/services/surfaceflinger/BufferLayerConsumer.h +++ b/services/surfaceflinger/BufferLayerConsumer.h @@ -140,6 +140,9 @@ public: // must be called from SF main thread const Region& getSurfaceDamage() const; + // Merge the given damage region into the current damage region value. + void mergeSurfaceDamage(const Region& damage); + // getCurrentApi retrieves the API which queues the current buffer. int getCurrentApi() const; @@ -220,8 +223,7 @@ private: // Utility class for managing GraphicBuffer references into renderengine class Image { public: - Image(sp<GraphicBuffer> graphicBuffer, renderengine::RenderEngine& engine) - : mGraphicBuffer(graphicBuffer), mRE(engine) {} + Image(const sp<GraphicBuffer>& graphicBuffer, renderengine::RenderEngine& engine); virtual ~Image(); const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; } diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index bd0b55f68..eb1bf66cd 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -143,7 +143,7 @@ bool BufferQueueLayer::framePresentTimeIsCurrent() const { } Mutex::Autolock lock(mQueueItemLock); - return mQueueItems[0].mTimestamp <= mFlinger->mScheduler->expectedPresentTime(); + return mQueueItems[0].mTimestamp <= mFlinger->getExpectedPresentTime(); } nsecs_t BufferQueueLayer::getDesiredPresentTime() { @@ -201,7 +201,7 @@ uint64_t BufferQueueLayer::getFrameNumber() const { uint64_t frameNumber = mQueueItems[0].mFrameNumber; // The head of the queue will be dropped if there are signaled and timely frames behind it - nsecs_t expectedPresentTime = mFlinger->mScheduler->expectedPresentTime(); + nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime(); if (isRemovedFromCurrentState()) { expectedPresentTime = 0; @@ -279,7 +279,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode, getTransformToDisplayInverse(), mFreezeGeometryUpdates); - nsecs_t expectedPresentTime = mFlinger->mScheduler->expectedPresentTime(); + nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime(); if (isRemovedFromCurrentState()) { expectedPresentTime = 0; @@ -316,6 +316,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t // and return early if (queuedBuffer) { Mutex::Autolock lock(mQueueItemLock); + mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage); mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber); mQueueItems.removeAt(0); mQueuedFrames--; @@ -351,6 +352,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t // Remove any stale buffers that have been dropped during // updateTexImage while (mQueueItems[0].mFrameNumber != currentFrameNumber) { + mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage); mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber); mQueueItems.removeAt(0); mQueuedFrames--; @@ -459,6 +461,7 @@ void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500)); if (result != NO_ERROR) { ALOGE("[%s] Timed out waiting on callback", mName.string()); + break; } } @@ -493,6 +496,7 @@ void BufferQueueLayer::onFrameReplaced(const BufferItem& item) { status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500)); if (result != NO_ERROR) { ALOGE("[%s] Timed out waiting on callback", mName.string()); + break; } } diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 05c721f14..63a07c35c 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -50,8 +50,12 @@ BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; mCurrentState.dataspace = ui::Dataspace::V0_SRGB; } + BufferStateLayer::~BufferStateLayer() { if (mActiveBuffer != nullptr) { + // Ensure that mActiveBuffer is uncached from RenderEngine here, as + // RenderEngine may have been using the buffer as an external texture + // after the client uncached the buffer. auto& engine(mFlinger->getRenderEngine()); engine.unbindExternalTextureBuffer(mActiveBuffer->getId()); } @@ -380,7 +384,7 @@ bool BufferStateLayer::framePresentTimeIsCurrent() const { return true; } - return mDesiredPresentTime <= mFlinger->mScheduler->expectedPresentTime(); + return mDesiredPresentTime <= mFlinger->getExpectedPresentTime(); } nsecs_t BufferStateLayer::getDesiredPresentTime() { @@ -571,11 +575,6 @@ status_t BufferStateLayer::updateActiveBuffer() { return BAD_VALUE; } - if (mActiveBuffer != nullptr) { - // todo: get this to work with BufferStateLayerCache - auto& engine(mFlinger->getRenderEngine()); - engine.unbindExternalTextureBuffer(mActiveBuffer->getId()); - } mActiveBuffer = s.buffer; mActiveBufferFence = s.acquireFence; auto& layerCompositionState = getCompositionLayer()->editState().frontEnd; diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 4e2bc4528..97e24cb27 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -34,6 +34,7 @@ class SlotGenerationTest; class BufferStateLayer : public BufferLayer { public: explicit BufferStateLayer(const LayerCreationArgs&); + ~BufferStateLayer() override; // ----------------------------------------------------------------------- diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp index 77f2f5765..16fe27c00 100644 --- a/services/surfaceflinger/ClientCache.cpp +++ b/services/surfaceflinger/ClientCache.cpp @@ -55,16 +55,16 @@ bool ClientCache::getBuffer(const client_cache_t& cacheId, return true; } -void ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) { +bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) { auto& [processToken, id] = cacheId; if (processToken == nullptr) { ALOGE("failed to cache buffer: invalid process token"); - return; + return false; } if (!buffer) { ALOGE("failed to cache buffer: invalid buffer"); - return; + return false; } std::lock_guard lock(mMutex); @@ -77,13 +77,13 @@ void ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& bu token = processToken.promote(); if (!token) { ALOGE("failed to cache buffer: invalid token"); - return; + return false; } status_t err = token->linkToDeath(mDeathRecipient); if (err != NO_ERROR) { ALOGE("failed to cache buffer: could not link to death"); - return; + return false; } auto [itr, success] = mBuffers.emplace(processToken, std::unordered_map<uint64_t, ClientCacheBuffer>()); @@ -95,10 +95,11 @@ void ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& bu if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) { ALOGE("failed to cache buffer: cache is full"); - return; + return false; } processBuffers[id].buffer = buffer; + return true; } void ClientCache::erase(const client_cache_t& cacheId) { @@ -139,16 +140,17 @@ sp<GraphicBuffer> ClientCache::get(const client_cache_t& cacheId) { return buf->buffer; } -void ClientCache::registerErasedRecipient(const client_cache_t& cacheId, +bool ClientCache::registerErasedRecipient(const client_cache_t& cacheId, const wp<ErasedRecipient>& recipient) { std::lock_guard lock(mMutex); ClientCacheBuffer* buf = nullptr; if (!getBuffer(cacheId, &buf)) { ALOGE("failed to register erased recipient, could not retrieve buffer"); - return; + return false; } buf->recipients.insert(recipient); + return true; } void ClientCache::unregisterErasedRecipient(const client_cache_t& cacheId, diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h index 9f057c45d..aa6c80dfa 100644 --- a/services/surfaceflinger/ClientCache.h +++ b/services/surfaceflinger/ClientCache.h @@ -36,7 +36,7 @@ class ClientCache : public Singleton<ClientCache> { public: ClientCache(); - void add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer); + bool add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer); void erase(const client_cache_t& cacheId); sp<GraphicBuffer> get(const client_cache_t& cacheId); @@ -48,7 +48,7 @@ public: virtual void bufferErased(const client_cache_t& clientCacheId) = 0; }; - void registerErasedRecipient(const client_cache_t& cacheId, + bool registerErasedRecipient(const client_cache_t& cacheId, const wp<ErasedRecipient>& recipient); void unregisterErasedRecipient(const client_cache_t& cacheId, const wp<ErasedRecipient>& recipient); diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp index 7927fa95b..e33bedbfc 100644 --- a/services/surfaceflinger/ContainerLayer.cpp +++ b/services/surfaceflinger/ContainerLayer.cpp @@ -35,11 +35,7 @@ bool ContainerLayer::isVisible() const { return false; } -bool ContainerLayer::canReceiveInput() const { - return !isHiddenByPolicy(); -} void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&, const Rect&, int32_t, const ui::Dataspace) {} - } // namespace android diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h index 7222a3e15..e6dbfcce4 100644 --- a/services/surfaceflinger/ContainerLayer.h +++ b/services/surfaceflinger/ContainerLayer.h @@ -31,8 +31,6 @@ public: const char* getTypeId() const override { return "ContainerLayer"; } bool isVisible() const override; - bool canReceiveInput() const override; - void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, const Rect& viewport, int32_t supportedPerFrameMetadata, const ui::Dataspace targetDataspace) override; diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index cc5a5b572..7f47a2ecd 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -24,6 +24,7 @@ #include <composer-command-buffer/2.2/ComposerCommandBuffer.h> #include <gui/BufferQueue.h> +#include <hidl/HidlTransportSupport.h> #include <hidl/HidlTransportUtils.h> namespace android { @@ -229,6 +230,7 @@ std::string Composer::dumpDebugInfo() void Composer::registerCallback(const sp<IComposerCallback>& callback) { + android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2); auto ret = mClient->registerCallback(callback); if (!ret.isOk()) { ALOGE("failed to register IComposerCallback"); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 1318bc0b2..44f3eae1e 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2037,11 +2037,18 @@ bool Layer::isRemovedFromCurrentState() const { return mRemovedFromCurrentState; } +// Debug helper for b/137560795 +#define INT32_MIGHT_OVERFLOW(n) (((n) >= INT32_MAX / 2) || ((n) <= INT32_MIN / 2)) + +#define RECT_BOUNDS_INVALID(rect) \ + (INT32_MIGHT_OVERFLOW((rect).left) || INT32_MIGHT_OVERFLOW((rect).right) || \ + INT32_MIGHT_OVERFLOW((rect).bottom) || INT32_MIGHT_OVERFLOW((rect).top)) + InputWindowInfo Layer::fillInputInfo() { InputWindowInfo info = mDrawingState.inputInfo; if (info.displayId == ADISPLAY_ID_NONE) { - info.displayId = mDrawingState.layerStack; + info.displayId = getLayerStack(); } ui::Transform t = getTransform(); @@ -2066,6 +2073,26 @@ InputWindowInfo Layer::fillInputInfo() { layerBounds = getCroppedBufferSize(getDrawingState()); } layerBounds = t.transform(layerBounds); + + // debug check for b/137560795 + { + if (RECT_BOUNDS_INVALID(layerBounds)) { + ALOGE("layer %s bounds are invalid (%" PRIi32 ", %" PRIi32 ", %" PRIi32 ", %" PRIi32 + ")", + mName.c_str(), layerBounds.left, layerBounds.top, layerBounds.right, + layerBounds.bottom); + std::string out; + getTransform().dump(out, "Transform"); + ALOGE("%s", out.c_str()); + layerBounds.left = layerBounds.top = layerBounds.right = layerBounds.bottom = 0; + } + + if (INT32_MIGHT_OVERFLOW(xSurfaceInset) || INT32_MIGHT_OVERFLOW(ySurfaceInset)) { + ALOGE("layer %s surface inset are invalid (%" PRIi32 ", %" PRIi32 ")", mName.c_str(), + int32_t(xSurfaceInset), int32_t(ySurfaceInset)); + xSurfaceInset = ySurfaceInset = 0; + } + } layerBounds.inset(xSurfaceInset, ySurfaceInset, xSurfaceInset, ySurfaceInset); // Input coordinate should match the layer bounds. @@ -2102,7 +2129,7 @@ std::shared_ptr<compositionengine::Layer> Layer::getCompositionLayer() const { } bool Layer::canReceiveInput() const { - return isVisible(); + return !isHiddenByPolicy(); } compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 8a80e15f2..b46eb112e 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -17,8 +17,6 @@ #ifndef ANDROID_LAYER_H #define ANDROID_LAYER_H -#include <sys/types.h> - #include <compositionengine/LayerFE.h> #include <gui/BufferQueue.h> #include <gui/ISurfaceComposerClient.h> @@ -28,6 +26,7 @@ #include <math/vec4.h> #include <renderengine/Mesh.h> #include <renderengine/Texture.h> +#include <sys/types.h> #include <ui/FloatRect.h> #include <ui/FrameStats.h> #include <ui/GraphicBuffer.h> @@ -44,16 +43,16 @@ #include <vector> #include "Client.h" +#include "ClientCache.h" +#include "DisplayHardware/ComposerHal.h" +#include "DisplayHardware/HWComposer.h" #include "FrameTracker.h" #include "LayerVector.h" #include "MonitoredProducer.h" +#include "RenderArea.h" #include "SurfaceFlinger.h" #include "TransactionCompletedThread.h" -#include "DisplayHardware/ComposerHal.h" -#include "DisplayHardware/HWComposer.h" -#include "RenderArea.h" - using namespace android::surfaceflinger; namespace android { diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 8e7386df0..80da15427 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -44,11 +44,14 @@ constexpr auto lumaSamplingStepTag = "LumaSamplingStep"; enum class samplingStep { noWorkNeeded, idleTimerWaiting, + waitForQuietFrame, waitForZeroPhase, waitForSamplePhase, sample }; +constexpr auto timeForRegionSampling = 5000000ns; +constexpr auto maxRegionSamplingSkips = 10; constexpr auto defaultRegionSamplingOffset = -3ms; constexpr auto defaultRegionSamplingPeriod = 100ms; constexpr auto defaultRegionSamplingTimerTimeout = 100ms; @@ -216,9 +219,9 @@ void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& lis void RegionSamplingThread::checkForStaleLuma() { std::lock_guard lock(mThreadControlMutex); - if (mDiscardedFrames) { + if (mDiscardedFrames > 0) { ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase)); - mDiscardedFrames = false; + mDiscardedFrames = 0; mPhaseCallback->startVsyncListener(); } } @@ -236,13 +239,25 @@ void RegionSamplingThread::doSample() { auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); if (lastSampleTime + mTunables.mSamplingPeriod > now) { ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting)); - mDiscardedFrames = true; + if (mDiscardedFrames == 0) mDiscardedFrames++; return; } + if (mDiscardedFrames < maxRegionSamplingSkips) { + // If there is relatively little time left for surfaceflinger + // until the next vsync deadline, defer this sampling work + // to a later frame, when hopefully there will be more time. + DisplayStatInfo stats; + mScheduler.getDisplayStatInfo(&stats); + if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) { + ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame)); + mDiscardedFrames++; + return; + } + } ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample)); - mDiscardedFrames = false; + mDiscardedFrames = 0; lastSampleTime = now; mIdleTimer.reset(); @@ -259,7 +274,7 @@ void RegionSamplingThread::binderDied(const wp<IBinder>& who) { namespace { // Using Rec. 709 primaries -float getLuma(float r, float g, float b) { +inline float getLuma(float r, float g, float b) { constexpr auto rec709_red_primary = 0.2126f; constexpr auto rec709_green_primary = 0.7152f; constexpr auto rec709_blue_primary = 0.0722f; @@ -294,10 +309,10 @@ float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t st const uint32_t* rowBase = data + row * stride; for (int32_t column = area.left; column < area.right; ++column) { uint32_t pixel = rowBase[column]; - const float r = (pixel & 0xFF) / 255.0f; - const float g = ((pixel >> 8) & 0xFF) / 255.0f; - const float b = ((pixel >> 16) & 0xFF) / 255.0f; - const uint8_t luma = std::round(getLuma(r, g, b) * 255.0f); + const float r = pixel & 0xFF; + const float g = (pixel >> 8) & 0xFF; + const float b = (pixel >> 16) & 0xFF; + const uint8_t luma = std::round(getLuma(r, g, b)); ++brightnessBuckets[luma]; if (brightnessBuckets[luma] > majoritySampleNum) return luma / 255.0f; } @@ -343,9 +358,19 @@ void RegionSamplingThread::captureSample() { } const auto device = mFlinger.getDefaultDisplayDevice(); - const auto display = device->getCompositionDisplay(); - const auto state = display->getState(); - const auto orientation = static_cast<ui::Transform::orientation_flags>(state.orientation); + const auto orientation = [](uint32_t orientation) { + switch (orientation) { + default: + case DisplayState::eOrientationDefault: + return ui::Transform::ROT_0; + case DisplayState::eOrientation90: + return ui::Transform::ROT_90; + case DisplayState::eOrientation180: + return ui::Transform::ROT_180; + case DisplayState::eOrientation270: + return ui::Transform::ROT_270; + } + }(device->getOrientation()); std::vector<RegionSamplingThread::Descriptor> descriptors; Region sampleRegion; diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h index 3c6fcf387..96ffe207e 100644 --- a/services/surfaceflinger/RegionSamplingThread.h +++ b/services/surfaceflinger/RegionSamplingThread.h @@ -117,7 +117,7 @@ private: std::condition_variable_any mCondition; bool mRunning GUARDED_BY(mThreadControlMutex) = true; bool mSampleRequested GUARDED_BY(mThreadControlMutex) = false; - bool mDiscardedFrames GUARDED_BY(mThreadControlMutex) = false; + uint32_t mDiscardedFrames GUARDED_BY(mThreadControlMutex) = 0; std::chrono::nanoseconds lastSampleTime GUARDED_BY(mThreadControlMutex); std::mutex mSamplingMutex; diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp index cd6fa4194..0c9405297 100644 --- a/services/surfaceflinger/Scheduler/DispSync.cpp +++ b/services/surfaceflinger/Scheduler/DispSync.cpp @@ -79,11 +79,7 @@ public: Mutex::Autolock lock(mMutex); mPhase = phase; - if (mReferenceTime != referenceTime) { - for (auto& eventListener : mEventListeners) { - eventListener.mHasFired = false; - } - } + const bool referenceTimeChanged = mReferenceTime != referenceTime; mReferenceTime = referenceTime; if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) { // Inflate the reference time to be the most recent predicted @@ -94,6 +90,16 @@ public: mReferenceTime = mReferenceTime + (numOldPeriods)*mPeriod; } mPeriod = period; + if (!mModelLocked && referenceTimeChanged) { + for (auto& eventListener : mEventListeners) { + eventListener.mLastEventTime = mReferenceTime + mPhase + eventListener.mPhase; + // If mLastEventTime is after mReferenceTime (can happen when positive phase offsets + // are used) we treat it as like it happened in previous period. + if (eventListener.mLastEventTime > mReferenceTime) { + eventListener.mLastEventTime -= mPeriod; + } + } + } if (mTraceDetailedInfo) { ATRACE_INT64("DispSync:Period", mPeriod); ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2); @@ -115,11 +121,13 @@ public: void lockModel() { Mutex::Autolock lock(mMutex); mModelLocked = true; + ATRACE_INT("DispSync:ModelLocked", mModelLocked); } void unlockModel() { Mutex::Autolock lock(mMutex); mModelLocked = false; + ATRACE_INT("DispSync:ModelLocked", mModelLocked); } virtual bool threadLoop() { @@ -284,12 +292,8 @@ public: // new offset to allow for a seamless offset change without double-firing or // skipping. nsecs_t diff = oldPhase - phase; - if (diff > mPeriod / 2) { - diff -= mPeriod; - } else if (diff < -mPeriod / 2) { - diff += mPeriod; - } eventListener.mLastEventTime -= diff; + eventListener.mLastCallbackTime -= diff; mCond.signal(); return NO_ERROR; } @@ -304,7 +308,6 @@ private: nsecs_t mLastEventTime; nsecs_t mLastCallbackTime; DispSync::Callback* mCallback; - bool mHasFired = false; }; struct CallbackInvocation { @@ -352,12 +355,7 @@ private: eventListener.mName); continue; } - if (eventListener.mHasFired && !mModelLocked) { - eventListener.mLastEventTime = t; - ALOGV("[%s] [%s] Skipping event due to already firing", mName, - eventListener.mName); - continue; - } + CallbackInvocation ci; ci.mCallback = eventListener.mCallback; ci.mEventTime = t; @@ -366,7 +364,6 @@ private: callbackInvocations.push_back(ci); eventListener.mLastEventTime = t; eventListener.mLastCallbackTime = now; - eventListener.mHasFired = true; } } @@ -493,7 +490,6 @@ void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) { ALOGE("Couldn't set SCHED_FIFO for DispSyncThread"); } - reset(); beginResync(); if (mTraceDetailedInfo && kEnableZeroPhaseTracer) { @@ -545,17 +541,15 @@ bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { void DispSync::beginResync() { Mutex::Autolock lock(mMutex); ALOGV("[%s] beginResync", mName); - mThread->unlockModel(); - mModelUpdated = false; - mNumResyncSamples = 0; + resetLocked(); } -bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodChanged) { +bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodFlushed) { Mutex::Autolock lock(mMutex); ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp)); - *periodChanged = false; + *periodFlushed = false; const size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES; mResyncSamples[idx] = timestamp; if (mNumResyncSamples == 0) { @@ -569,16 +563,20 @@ bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodChanged) { const nsecs_t lastTimestamp = mResyncSamples[priorIdx]; const nsecs_t observedVsync = std::abs(timestamp - lastTimestamp); - if (std::abs(observedVsync - mPendingPeriod) < std::abs(observedVsync - mPeriod)) { - // Observed vsync is closer to the pending period, so reset the - // model and flush the pending period. + if (std::abs(observedVsync - mPendingPeriod) <= std::abs(observedVsync - mIntendedPeriod)) { + // Either the observed vsync is closer to the pending period, (and + // thus we detected a period change), or the period change will + // no-op. In either case, reset the model and flush the pending + // period. resetLocked(); + mIntendedPeriod = mPendingPeriod; mPeriod = mPendingPeriod; mPendingPeriod = 0; if (mTraceDetailedInfo) { ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod); + ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod); } - *periodChanged = true; + *periodFlushed = true; } } // Always update the reference time with the most recent timestamp. @@ -609,6 +607,7 @@ bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodChanged) { bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0; ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked"); if (modelLocked) { + *periodFlushed = true; mThread->lockModel(); } return !modelLocked; @@ -643,10 +642,17 @@ status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) { void DispSync::setPeriod(nsecs_t period) { Mutex::Autolock lock(mMutex); + + const bool pendingPeriodShouldChange = + period != mIntendedPeriod || (period == mIntendedPeriod && mPendingPeriod != 0); + + if (pendingPeriodShouldChange) { + mPendingPeriod = period; + } if (mTraceDetailedInfo) { - ATRACE_INT("DispSync:PendingPeriod", period); + ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod); + ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod); } - mPendingPeriod = period; } nsecs_t DispSync::getPeriod() { @@ -662,7 +668,13 @@ void DispSync::updateModelLocked() { nsecs_t durationSum = 0; nsecs_t minDuration = INT64_MAX; nsecs_t maxDuration = 0; - for (size_t i = 1; i < mNumResyncSamples; i++) { + // We skip the first 2 samples because the first vsync duration on some + // devices may be much more inaccurate than on other devices, e.g. due + // to delays in ramping up from a power collapse. By doing so this + // actually increases the accuracy of the DispSync model even though + // we're effectively relying on fewer sample points. + static constexpr size_t numSamplesSkipped = 2; + for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) { size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES; nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev]; @@ -673,15 +685,14 @@ void DispSync::updateModelLocked() { // Exclude the min and max from the average durationSum -= minDuration + maxDuration; - mPeriod = durationSum / (mNumResyncSamples - 3); + mPeriod = durationSum / (mNumResyncSamples - numSamplesSkipped - 2); ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod)); double sampleAvgX = 0; double sampleAvgY = 0; double scale = 2.0 * M_PI / double(mPeriod); - // Intentionally skip the first sample - for (size_t i = 1; i < mNumResyncSamples; i++) { + for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) { size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; nsecs_t sample = mResyncSamples[idx] - mReferenceTime; double samplePhase = double(sample % mPeriod) * scale; @@ -689,8 +700,8 @@ void DispSync::updateModelLocked() { sampleAvgY += sin(samplePhase); } - sampleAvgX /= double(mNumResyncSamples - 1); - sampleAvgY /= double(mNumResyncSamples - 1); + sampleAvgX /= double(mNumResyncSamples - numSamplesSkipped); + sampleAvgY /= double(mNumResyncSamples - numSamplesSkipped); mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale); @@ -764,6 +775,9 @@ void DispSync::resetErrorLocked() { mPresentSampleOffset = 0; mError = 0; mZeroErrSamplesCount = 0; + if (mTraceDetailedInfo) { + ATRACE_INT64("DispSync:Error", mError); + } for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { mPresentFences[i] = FenceTime::NO_FENCE; } diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h index 8f8b8e7a9..3e33c7edc 100644 --- a/services/surfaceflinger/Scheduler/DispSync.h +++ b/services/surfaceflinger/Scheduler/DispSync.h @@ -49,7 +49,7 @@ public: virtual void reset() = 0; virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0; virtual void beginResync() = 0; - virtual bool addResyncSample(nsecs_t timestamp, bool* periodChanged) = 0; + virtual bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) = 0; virtual void endResync() = 0; virtual void setPeriod(nsecs_t period) = 0; virtual nsecs_t getPeriod() = 0; @@ -120,17 +120,19 @@ public: // from the hardware vsync events. void beginResync() override; // Adds a vsync sample to the dispsync model. The timestamp is the time - // of the vsync event that fired. periodChanged will return true if the + // of the vsync event that fired. periodFlushed will return true if the // vsync period was detected to have changed to mPendingPeriod. // // This method will return true if more vsync samples are needed to lock // down the DispSync model, and false otherwise. - bool addResyncSample(nsecs_t timestamp, bool* periodChanged) override; + // periodFlushed will be set to true if mPendingPeriod is flushed to + // mIntendedPeriod, and false otherwise. + bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) override; void endResync() override; // The setPeriod method sets the vsync event model's period to a specific - // value. This should be used to prime the model when a display is first - // turned on. It should NOT be used after that. + // value. This should be used to prime the model when a display is first + // turned on, or when a refresh rate change is requested. void setPeriod(nsecs_t period) override; // The getPeriod method returns the current vsync period. @@ -205,6 +207,11 @@ private: // nanoseconds. nsecs_t mPeriod; + // mIntendedPeriod is the intended period of the modeled vsync events in + // nanoseconds. Under ideal conditions this should be similar if not the + // same as mPeriod, plus or minus an observed error. + nsecs_t mIntendedPeriod = 0; + // mPendingPeriod is the proposed period change in nanoseconds. // If mPendingPeriod differs from mPeriod and is nonzero, it will // be flushed to mPeriod when we detect that the hardware switched @@ -236,8 +243,8 @@ private: // process to store information about the hardware vsync event times used // to compute the model. nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES] = {0}; - size_t mFirstResyncSample; - size_t mNumResyncSamples; + size_t mFirstResyncSample = 0; + size_t mNumResyncSamples = 0; int mNumResyncSamplesSincePresent; // These member variables store information about the present fences used diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp index 00948aedb..5faf46e31 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp +++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp @@ -27,18 +27,23 @@ namespace android { -DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, +DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, + nsecs_t offsetThresholdForNextVsync, bool traceVsync, const char* name) : mName(name), mTraceVsync(traceVsync), mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)), mVsyncEventLabel(base::StringPrintf("VSYNC-%s", name)), + mVsyncOffsetLabel(base::StringPrintf("VsyncOffset-%s", name)), + mVsyncNegativeOffsetLabel(base::StringPrintf("VsyncNegativeOffset-%s", name)), mDispSync(dispSync), - mPhaseOffset(phaseOffset) {} + mPhaseOffset(phaseOffset), + mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {} void DispSyncSource::setVSyncEnabled(bool enable) { std::lock_guard lock(mVsyncMutex); if (enable) { + tracePhaseOffset(); status_t err = mDispSync->addEventListener(mName, mPhaseOffset, static_cast<DispSync::Callback*>(this), mLastCallbackTime); @@ -64,16 +69,21 @@ void DispSyncSource::setCallback(VSyncSource::Callback* callback) { void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) { std::lock_guard lock(mVsyncMutex); + const nsecs_t period = mDispSync->getPeriod(); + // Check if offset should be handled as negative + if (phaseOffset >= mOffsetThresholdForNextVsync) { + phaseOffset -= period; + } - // Normalize phaseOffset to [0, period) - auto period = mDispSync->getPeriod(); - phaseOffset %= period; - if (phaseOffset < 0) { - // If we're here, then phaseOffset is in (-period, 0). After this - // operation, it will be in (0, period) - phaseOffset += period; + // Normalize phaseOffset to [-period, period) + const int numPeriods = phaseOffset / period; + phaseOffset -= numPeriods * period; + if (mPhaseOffset == phaseOffset) { + return; } + mPhaseOffset = phaseOffset; + tracePhaseOffset(); // If we're not enabled, we don't need to mess with the listeners if (!mEnabled) { @@ -92,11 +102,11 @@ void DispSyncSource::onDispSyncEvent(nsecs_t when) { { std::lock_guard lock(mCallbackMutex); callback = mCallback; + } - if (mTraceVsync) { - mValue = (mValue + 1) % 2; - ATRACE_INT(mVsyncEventLabel.c_str(), mValue); - } + if (mTraceVsync) { + mValue = (mValue + 1) % 2; + ATRACE_INT(mVsyncEventLabel.c_str(), mValue); } if (callback != nullptr) { @@ -104,4 +114,14 @@ void DispSyncSource::onDispSyncEvent(nsecs_t when) { } } -} // namespace android
\ No newline at end of file +void DispSyncSource::tracePhaseOffset() { + if (mPhaseOffset > 0) { + ATRACE_INT(mVsyncOffsetLabel.c_str(), mPhaseOffset); + ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), 0); + } else { + ATRACE_INT(mVsyncOffsetLabel.c_str(), 0); + ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), -mPhaseOffset); + } +} + +} // namespace android diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h index 4759699c5..50560a5a2 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.h +++ b/services/surfaceflinger/Scheduler/DispSyncSource.h @@ -25,7 +25,8 @@ namespace android { class DispSyncSource final : public VSyncSource, private DispSync::Callback { public: - DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name); + DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync, + bool traceVsync, const char* name); ~DispSyncSource() override = default; @@ -38,12 +39,16 @@ private: // The following method is the implementation of the DispSync::Callback. virtual void onDispSyncEvent(nsecs_t when); + void tracePhaseOffset() REQUIRES(mVsyncMutex); + const char* const mName; int mValue = 0; const bool mTraceVsync; const std::string mVsyncOnLabel; const std::string mVsyncEventLabel; + const std::string mVsyncOffsetLabel; + const std::string mVsyncNegativeOffsetLabel; nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0; DispSync* mDispSync; @@ -53,7 +58,8 @@ private: std::mutex mVsyncMutex; nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex); + const nsecs_t mOffsetThresholdForNextVsync; bool mEnabled GUARDED_BY(mVsyncMutex) = false; }; -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 05bad4ddd..9d1f77785 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -76,6 +76,10 @@ std::string toString(const DisplayEventReceiver::Event& event) { return StringPrintf("VSync{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%u}", event.header.displayId, event.vsync.count); + case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: + return StringPrintf("ConfigChanged{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT + ", configId=%u}", + event.header.displayId, event.config.configId); default: return "Event{}"; } @@ -107,8 +111,10 @@ DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId, int32 } // namespace EventThreadConnection::EventThreadConnection(EventThread* eventThread, - ResyncCallback resyncCallback) + ResyncCallback resyncCallback, + ISurfaceComposer::ConfigChanged configChanged) : resyncCallback(std::move(resyncCallback)), + configChanged(configChanged), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {} @@ -203,8 +209,10 @@ void EventThread::setPhaseOffset(nsecs_t phaseOffset) { mVSyncSource->setPhaseOffset(phaseOffset); } -sp<EventThreadConnection> EventThread::createEventConnection(ResyncCallback resyncCallback) const { - return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback)); +sp<EventThreadConnection> EventThread::createEventConnection( + ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const { + return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback), + configChanged); } status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) { @@ -398,9 +406,11 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, const sp<EventThreadConnection>& connection) const { switch (event.header.type) { case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: - case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: return true; + case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: + return connection->configChanged == ISurfaceComposer::eConfigChangedDispatch; + case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: switch (connection->vsyncRequest) { case VSyncRequest::None: diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 61530c62e..dd23b8872 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -69,7 +69,8 @@ public: class EventThreadConnection : public BnDisplayEventConnection { public: - EventThreadConnection(EventThread*, ResyncCallback); + EventThreadConnection(EventThread*, ResyncCallback, + ISurfaceComposer::ConfigChanged configChanged); virtual ~EventThreadConnection(); virtual status_t postEvent(const DisplayEventReceiver::Event& event); @@ -82,6 +83,7 @@ public: const ResyncCallback resyncCallback; VSyncRequest vsyncRequest = VSyncRequest::None; + const ISurfaceComposer::ConfigChanged configChanged; private: virtual void onFirstRef(); @@ -93,7 +95,8 @@ class EventThread { public: virtual ~EventThread(); - virtual sp<EventThreadConnection> createEventConnection(ResyncCallback) const = 0; + virtual sp<EventThreadConnection> createEventConnection( + ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const = 0; // called before the screen is turned off from main thread virtual void onScreenReleased() = 0; @@ -128,7 +131,8 @@ public: EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback, const char* threadName); ~EventThread(); - sp<EventThreadConnection> createEventConnection(ResyncCallback) const override; + sp<EventThreadConnection> createEventConnection( + ResyncCallback, ISurfaceComposer::ConfigChanged configChanged) const override; status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override; void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override; diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 1db43a32c..f80c2336f 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -46,11 +46,13 @@ LayerHistory::LayerHistory() { LayerHistory::~LayerHistory() = default; std::unique_ptr<LayerHistory::LayerHandle> LayerHistory::createLayer(const std::string name, + float minRefreshRate, float maxRefreshRate) { const int64_t id = sNextId++; std::lock_guard lock(mLock); - mInactiveLayerInfos.emplace(id, std::make_shared<LayerInfo>(name, maxRefreshRate)); + mInactiveLayerInfos.emplace(id, + std::make_shared<LayerInfo>(name, minRefreshRate, maxRefreshRate)); return std::make_unique<LayerHistory::LayerHandle>(*this, id); } @@ -173,5 +175,18 @@ void LayerHistory::removeIrrelevantLayers() { } } +void LayerHistory::clearHistory() { + std::lock_guard lock(mLock); + + auto it = mActiveLayerInfos.begin(); + while (it != mActiveLayerInfos.end()) { + auto id = it->first; + auto layerInfo = it->second; + layerInfo->clearHistory(); + mInactiveLayerInfos.insert({id, layerInfo}); + it = mActiveLayerInfos.erase(it); + } +} + } // namespace scheduler } // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index adc5ce5f6..5598cc1cf 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -53,7 +53,8 @@ public: ~LayerHistory(); // When the layer is first created, register it. - std::unique_ptr<LayerHandle> createLayer(const std::string name, float maxRefreshRate); + std::unique_ptr<LayerHandle> createLayer(const std::string name, float minRefreshRate, + float maxRefreshRate); // Method for inserting layers and their requested present time into the unordered map. void insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime, bool isHdr); @@ -64,6 +65,9 @@ public: // layers. See go/content-fps-detection-in-scheduler for more information. std::pair<float, bool> getDesiredRefreshRateAndHDR(); + // Clears all layer history. + void clearHistory(); + // Removes the handle and the object from the map. void destroyLayer(const int64_t id); diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 95d7d3156..723d71ff3 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -24,9 +24,10 @@ namespace android { namespace scheduler { -LayerInfo::LayerInfo(const std::string name, float maxRefreshRate) +LayerInfo::LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate) : mName(name), mMinRefreshDuration(1e9f / maxRefreshRate), + mLowActivityRefreshDuration(1e9f / minRefreshRate), mRefreshRateHistory(mMinRefreshDuration) {} LayerInfo::~LayerInfo() = default; @@ -38,12 +39,19 @@ void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime) { mLastUpdatedTime = std::max(lastPresentTime, systemTime()); mPresentTimeHistory.insertPresentTime(mLastUpdatedTime); + if (mLastPresentTime == 0) { + // First frame + mLastPresentTime = lastPresentTime; + return; + } + const nsecs_t timeDiff = lastPresentTime - mLastPresentTime; mLastPresentTime = lastPresentTime; // Ignore time diff that are too high - those are stale values - if (timeDiff > TIME_EPSILON_NS.count()) return; - const nsecs_t refreshDuration = (timeDiff > 0) ? timeDiff : mMinRefreshDuration; - mRefreshRateHistory.insertRefreshRate(refreshDuration); + if (timeDiff > OBSOLETE_TIME_EPSILON_NS.count()) return; + const nsecs_t refreshDuration = std::max(timeDiff, mMinRefreshDuration); + const int fps = 1e9f / refreshDuration; + mRefreshRateHistory.insertRefreshRate(fps); } } // namespace scheduler diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index 02b6aefa2..17afddac2 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -46,7 +46,7 @@ class LayerInfo { public: explicit RefreshRateHistory(nsecs_t minRefreshDuration) : mMinRefreshDuration(minRefreshDuration) {} - void insertRefreshRate(nsecs_t refreshRate) { + void insertRefreshRate(int refreshRate) { mElements.push_back(refreshRate); if (mElements.size() > HISTORY_SIZE) { mElements.pop_front(); @@ -54,13 +54,13 @@ class LayerInfo { } float getRefreshRateAvg() const { - nsecs_t refreshDuration = mMinRefreshDuration; - if (mElements.size() == HISTORY_SIZE) { - refreshDuration = scheduler::calculate_mean(mElements); + if (mElements.empty()) { + return 1e9f / mMinRefreshDuration; } - return 1e9f / refreshDuration; + return scheduler::calculate_mean(mElements); } + void clearHistory() { mElements.clear(); } private: @@ -86,13 +86,43 @@ class LayerInfo { // Checks whether the present time that was inserted HISTORY_SIZE ago is within a // certain threshold: TIME_EPSILON_NS. bool isRelevant() const { - const int64_t obsoleteEpsilon = systemTime() - scheduler::TIME_EPSILON_NS.count(); - // The layer had to publish at least HISTORY_SIZE of updates, and the first - // update should not be older than TIME_EPSILON_NS nanoseconds. - if (mElements.size() == HISTORY_SIZE && - mElements.at(HISTORY_SIZE - 1) > obsoleteEpsilon) { + if (mElements.size() < 2) { + return false; + } + + // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates + if (mElements.size() != HISTORY_SIZE && + mElements.at(mElements.size() - 1) - mElements.at(0) < HISTORY_TIME.count()) { + return false; + } + + // The last update should not be older than OBSOLETE_TIME_EPSILON_NS nanoseconds. + const int64_t obsoleteEpsilon = + systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count(); + if (mElements.at(mElements.size() - 1) < obsoleteEpsilon) { + return false; + } + + return true; + } + + bool isLowActivityLayer() const { + // We want to make sure that we received more than two frames from the layer + // in order to check low activity. + if (mElements.size() < scheduler::LOW_ACTIVITY_BUFFERS + 1) { + return false; + } + + const int64_t obsoleteEpsilon = + systemTime() - scheduler::LOW_ACTIVITY_EPSILON_NS.count(); + // Check the frame before last to determine whether there is low activity. + // If that frame is older than LOW_ACTIVITY_EPSILON_NS, the layer is sending + // infrequent updates. + if (mElements.at(mElements.size() - (scheduler::LOW_ACTIVITY_BUFFERS + 1)) < + obsoleteEpsilon) { return true; } + return false; } @@ -100,11 +130,12 @@ class LayerInfo { private: std::deque<nsecs_t> mElements; - static constexpr size_t HISTORY_SIZE = 10; + static constexpr size_t HISTORY_SIZE = 90; + static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s; }; public: - LayerInfo(const std::string name, float maxRefreshRate); + LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate); ~LayerInfo(); LayerInfo(const LayerInfo&) = delete; @@ -134,6 +165,10 @@ public: // Calculate the average refresh rate. float getDesiredRefreshRate() const { std::lock_guard lock(mLock); + + if (mPresentTimeHistory.isLowActivityLayer()) { + return 1e9f / mLowActivityRefreshDuration; + } return mRefreshRateHistory.getRefreshRateAvg(); } @@ -165,6 +200,7 @@ public: private: const std::string mName; const nsecs_t mMinRefreshDuration; + const nsecs_t mLowActivityRefreshDuration; mutable std::mutex mLock; nsecs_t mLastUpdatedTime GUARDED_BY(mLock) = 0; nsecs_t mLastPresentTime GUARDED_BY(mLock) = 0; diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index baf900df5..fcb307f21 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -96,7 +96,8 @@ void MessageQueue::setEventThread(android::EventThread* eventThread, } mEventThread = eventThread; - mEvents = eventThread->createEventConnection(std::move(resyncCallback)); + mEvents = eventThread->createEventConnection(std::move(resyncCallback), + ISurfaceComposer::eConfigChangedSuppress); mEvents->stealReceiveChannel(&mEventTube); mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver, this); diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp index 276bce1f8..9fa2bbc96 100644 --- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp +++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp @@ -25,6 +25,7 @@ using namespace android::sysprop; namespace scheduler { +using RefreshRateType = RefreshRateConfigs::RefreshRateType; PhaseOffsets::~PhaseOffsets() = default; namespace impl { @@ -72,25 +73,31 @@ PhaseOffsets::PhaseOffsets() { property_get("debug.sf.phase_offset_threshold_for_next_vsync_ns", value, "-1"); const int phaseOffsetThresholdForNextVsyncNs = atoi(value); - mDefaultRefreshRateOffsets.early = {earlySfOffsetNs != -1 ? earlySfOffsetNs - : sfVsyncPhaseOffsetNs, - earlyAppOffsetNs != -1 ? earlyAppOffsetNs - : vsyncPhaseOffsetNs}; - mDefaultRefreshRateOffsets.earlyGl = {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs - : sfVsyncPhaseOffsetNs, - earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs - : vsyncPhaseOffsetNs}; - mDefaultRefreshRateOffsets.late = {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs}; - - mHighRefreshRateOffsets.early = {highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs - : highFpsLateSfOffsetNs, - highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs - : highFpsLateAppOffsetNs}; - mHighRefreshRateOffsets.earlyGl = {highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs - : highFpsLateSfOffsetNs, - highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs - : highFpsLateAppOffsetNs}; - mHighRefreshRateOffsets.late = {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs}; + Offsets defaultOffsets; + Offsets highFpsOffsets; + defaultOffsets.early = {RefreshRateType::DEFAULT, + earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs, + earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs}; + defaultOffsets.earlyGl = {RefreshRateType::DEFAULT, + earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs, + earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs}; + defaultOffsets.late = {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs}; + + highFpsOffsets.early = {RefreshRateType::PERFORMANCE, + highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs + : highFpsLateSfOffsetNs, + highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs + : highFpsLateAppOffsetNs}; + highFpsOffsets.earlyGl = {RefreshRateType::PERFORMANCE, + highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs + : highFpsLateSfOffsetNs, + highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs + : highFpsLateAppOffsetNs}; + highFpsOffsets.late = {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs, + highFpsLateAppOffsetNs}; + + mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets}); + mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets}); mOffsetThresholdForNextVsync = phaseOffsetThresholdForNextVsyncNs != -1 ? phaseOffsetThresholdForNextVsyncNs @@ -99,12 +106,7 @@ PhaseOffsets::PhaseOffsets() { PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate( android::scheduler::RefreshRateConfigs::RefreshRateType refreshRateType) const { - switch (refreshRateType) { - case RefreshRateConfigs::RefreshRateType::PERFORMANCE: - return mHighRefreshRateOffsets; - default: - return mDefaultRefreshRateOffsets; - } + return mOffsets.at(refreshRateType); } void PhaseOffsets::dump(std::string& result) const { diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h index dc71e6eb6..2b5c2f10f 100644 --- a/services/surfaceflinger/Scheduler/PhaseOffsets.h +++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h @@ -17,6 +17,7 @@ #pragma once #include <cinttypes> +#include <unordered_map> #include "RefreshRateConfigs.h" #include "VSyncModulator.h" @@ -79,14 +80,10 @@ public: void dump(std::string& result) const override; private: - Offsets getDefaultRefreshRateOffsets() { return mDefaultRefreshRateOffsets; } - Offsets getHighRefreshRateOffsets() { return mHighRefreshRateOffsets; } - std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType = RefreshRateConfigs::RefreshRateType::DEFAULT; - Offsets mDefaultRefreshRateOffsets; - Offsets mHighRefreshRateOffsets; + std::unordered_map<RefreshRateConfigs::RefreshRateType, Offsets> mOffsets; nsecs_t mOffsetThresholdForNextVsync; }; } // namespace impl diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index d7300583c..d8137085d 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -34,10 +34,9 @@ namespace scheduler { */ class RefreshRateConfigs { public: - // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest - // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance + // Enum to indicate which vsync rate to run at. Default is the old 60Hz, and performance // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs. - enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE }; + enum class RefreshRateType { DEFAULT, PERFORMANCE }; struct RefreshRate { // This config ID corresponds to the position of the config in the vector that is stored @@ -47,26 +46,57 @@ public: std::string name; // Refresh rate in frames per second, rounded to the nearest integer. uint32_t fps = 0; - // config Id (returned from HWC2::Display::Config::getId()) - hwc2_config_t id; + // Vsync period in nanoseconds. + nsecs_t vsyncPeriod; + // Hwc config Id (returned from HWC2::Display::Config::getId()) + hwc2_config_t hwcId; }; + // Returns true if this device is doing refresh rate switching. This won't change at runtime. + bool refreshRateSwitchingSupported() const { return mRefreshRateSwitchingSupported; } + + // Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access + // from multiple threads. This can only be called if refreshRateSwitching() returns true. // TODO(b/122916473): Get this information from configs prepared by vendors, instead of // baking them in. - const std::map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() const { - return mRefreshRates; + const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const { + LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported); + return mRefreshRateMap; } - std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) const { - const auto& refreshRate = mRefreshRates.find(type); - if (refreshRate != mRefreshRates.end()) { + + const RefreshRate& getRefreshRateFromType(RefreshRateType type) const { + if (!mRefreshRateSwitchingSupported) { + return getCurrentRefreshRate().second; + } else { + auto refreshRate = mRefreshRateMap.find(type); + LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end()); return refreshRate->second; } - return nullptr; } - RefreshRateType getRefreshRateType(hwc2_config_t id) const { - for (const auto& [type, refreshRate] : mRefreshRates) { - if (refreshRate->id == id) { + std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const { + int currentConfig = mCurrentConfig; + if (mRefreshRateSwitchingSupported) { + for (const auto& [type, refresh] : mRefreshRateMap) { + if (refresh.configId == currentConfig) { + return {type, refresh}; + } + } + LOG_ALWAYS_FATAL(); + } + return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]}; + } + + const RefreshRate& getRefreshRateFromConfigId(int configId) const { + LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size()); + return mRefreshRates[configId]; + } + + RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const { + if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT; + + for (const auto& [type, refreshRate] : mRefreshRateMap) { + if (refreshRate.hwcId == hwcId) { return type; } } @@ -74,64 +104,102 @@ public: return RefreshRateType::DEFAULT; } - void populate(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) { - mRefreshRates.clear(); + void setCurrentConfig(int config) { + LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size()); + mCurrentConfig = config; + } - // This is the rate that HWC encapsulates right now when the device is in DOZE mode. - mRefreshRates.emplace(RefreshRateType::POWER_SAVING, - std::make_shared<RefreshRate>( - RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, - HWC2_SCREEN_OFF_CONFIG_ID})); + struct InputConfig { + hwc2_config_t hwcId = 0; + nsecs_t vsyncPeriod = 0; + }; - if (configs.size() < 1) { - ALOGE("Device does not have valid configs. Config size is 0."); - return; + RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs, + int currentConfig) { + init(refreshRateSwitching, configs, currentConfig); + } + + RefreshRateConfigs(bool refreshRateSwitching, + const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, + int currentConfig) { + std::vector<InputConfig> inputConfigs; + for (const auto& config : configs) { + inputConfigs.push_back({config->getId(), config->getVsyncPeriod()}); } + init(refreshRateSwitching, inputConfigs, currentConfig); + } + +private: + void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs, + int currentConfig) { + mRefreshRateSwitchingSupported = refreshRateSwitching; + LOG_ALWAYS_FATAL_IF(configs.empty()); + LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size()); + mCurrentConfig = currentConfig; + + auto buildRefreshRate = [&](int configId) -> RefreshRate { + const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod; + const float fps = 1e9 / vsyncPeriod; + return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps), + vsyncPeriod, configs[configId].hwcId}; + }; - // Create a map between config index and vsync period. This is all the info we need - // from the configs. - std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod; for (int i = 0; i < configs.size(); ++i) { - configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod()); + mRefreshRates.push_back(buildRefreshRate(i)); } - std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(), - [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) { - return a.second > b.second; - }); + if (!mRefreshRateSwitchingSupported) return; - // When the configs are ordered by the resync rate. We assume that the first one is DEFAULT. - nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second; - if (vsyncPeriod != 0) { - const float fps = 1e9 / vsyncPeriod; - const int configId = configIdToVsyncPeriod[0].first; - mRefreshRates.emplace(RefreshRateType::DEFAULT, - std::make_shared<RefreshRate>( - RefreshRate{configId, base::StringPrintf("%2.ffps", fps), - static_cast<uint32_t>(fps), - configs.at(configId)->getId()})); - } + auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> { + if (configs.size() < 2) { + return {}; + } - if (configs.size() < 2) { + std::vector<const RefreshRate*> sortedRefreshRates; + for (const auto& refreshRate : mRefreshRates) { + sortedRefreshRates.push_back(&refreshRate); + } + std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(), + [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) { + return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod; + }); + + // When the configs are ordered by the resync rate, we assume that + // the first one is DEFAULT and the second one is PERFORMANCE, + // i.e. the higher rate. + if (sortedRefreshRates[0]->vsyncPeriod == 0 || + sortedRefreshRates[1]->vsyncPeriod == 0) { + return {}; + } + + return std::pair<int, int>(sortedRefreshRates[0]->configId, + sortedRefreshRates[1]->configId); + }; + + auto defaultAndPerfConfigs = findDefaultAndPerfConfigs(); + if (!defaultAndPerfConfigs) { + mRefreshRateSwitchingSupported = false; return; } - // When the configs are ordered by the resync rate. We assume that the second one is - // PERFORMANCE, eg. the higher rate. - vsyncPeriod = configIdToVsyncPeriod[1].second; - if (vsyncPeriod != 0) { - const float fps = 1e9 / vsyncPeriod; - const int configId = configIdToVsyncPeriod[1].first; - mRefreshRates.emplace(RefreshRateType::PERFORMANCE, - std::make_shared<RefreshRate>( - RefreshRate{configId, base::StringPrintf("%2.ffps", fps), - static_cast<uint32_t>(fps), - configs.at(configId)->getId()})); - } + mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first]; + mRefreshRateMap[RefreshRateType::PERFORMANCE] = + mRefreshRates[defaultAndPerfConfigs->second]; } -private: - std::map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates; + // Whether this device is doing refresh rate switching or not. This must not change after this + // object is initialized. + bool mRefreshRateSwitchingSupported; + // The list of refresh rates, indexed by display config ID. This must not change after this + // object is initialized. + std::vector<RefreshRate> mRefreshRates; + // The mapping of refresh rate type to RefreshRate. This must not change after this object is + // initialized. + std::map<RefreshRateType, RefreshRate> mRefreshRateMap; + // The ID of the current config. This will change at runtime. This is set by SurfaceFlinger on + // the main thread, and read by the Scheduler (and other objects) on other threads, so it's + // atomic. + std::atomic<int> mCurrentConfig; }; } // namespace scheduler diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h index 7e7c6307a..947eb08d9 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateStats.h +++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h @@ -41,21 +41,18 @@ class RefreshRateStats { static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR; public: - RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats) - : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats) {} - - // Sets power mode. We only collect the information when the power mode is not - // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based - // on config mode. + RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats, + int currentConfigMode, int currentPowerMode) + : mRefreshRateConfigs(refreshRateConfigs), + mTimeStats(timeStats), + mCurrentConfigMode(currentConfigMode), + mCurrentPowerMode(currentPowerMode) {} + + // Sets power mode. void setPowerMode(int mode) { if (mCurrentPowerMode == mode) { return; } - // If power mode is normal, the time is going to be recorded under config modes. - if (mode == HWC_POWER_MODE_NORMAL) { - mCurrentPowerMode = mode; - return; - } flushTime(); mCurrentPowerMode = mode; } @@ -79,16 +76,15 @@ public: flushTime(); std::unordered_map<std::string, int64_t> totalTime; - for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) { - int64_t totalTimeForConfig = 0; - if (!config) { - continue; - } - if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) { - totalTimeForConfig = mConfigModesTotalTime.at(config->configId); - } - totalTime[config->name] = totalTimeForConfig; + // Multiple configs may map to the same name, e.g. "60fps". Add the + // times for such configs together. + for (const auto& [config, time] : mConfigModesTotalTime) { + totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] = 0; + } + for (const auto& [config, time] : mConfigModesTotalTime) { + totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] += time; } + totalTime["ScreenOff"] = mScreenOffTime; return totalTime; } @@ -104,32 +100,26 @@ public: } private: - void flushTime() { - // Normal power mode is counted under different config modes. - if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) { - flushTimeForMode(mCurrentConfigMode); - } else { - flushTimeForMode(SCREEN_OFF_CONFIG_ID); - } - } - // Calculates the time that passed in ms between the last time we recorded time and the time // this method was called. - void flushTimeForMode(int mode) { + void flushTime() { nsecs_t currentTime = systemTime(); nsecs_t timeElapsed = currentTime - mPreviousRecordedTime; int64_t timeElapsedMs = ns2ms(timeElapsed); mPreviousRecordedTime = currentTime; - mConfigModesTotalTime[mode] += timeElapsedMs; - for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) { - if (!config) { - continue; - } - if (config->configId == mode) { - mTimeStats.recordRefreshRate(config->fps, timeElapsed); + uint32_t fps = 0; + if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) { + // Normal power mode is counted under different config modes. + if (mConfigModesTotalTime.find(mCurrentConfigMode) == mConfigModesTotalTime.end()) { + mConfigModesTotalTime[mCurrentConfigMode] = 0; } + mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs; + fps = mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps; + } else { + mScreenOffTime += timeElapsedMs; } + mTimeStats.recordRefreshRate(fps, timeElapsed); } // Formats the time in milliseconds into easy to read format. @@ -149,10 +139,11 @@ private: // Aggregate refresh rate statistics for telemetry. TimeStats& mTimeStats; - int64_t mCurrentConfigMode = SCREEN_OFF_CONFIG_ID; - int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF; + int mCurrentConfigMode; + int32_t mCurrentPowerMode; - std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime; + std::unordered_map<int /* config */, int64_t /* duration in ms */> mConfigModesTotalTime; + int64_t mScreenOffTime = 0; nsecs_t mPreviousRecordedTime = systemTime(); }; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 513436a27..0b4337839 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -76,6 +76,7 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, mSupportKernelTimer = support_kernel_idle_timer(false); mSetTouchTimerMs = set_touch_timer_ms(0); + mSetDisplayPowerTimerMs = set_display_power_timer_ms(0); char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.set_idle_timer_ms", value, "0"); @@ -110,26 +111,38 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, [this] { expiredTouchTimerCallback(); }); mTouchTimer->start(); } + + if (mSetDisplayPowerTimerMs > 0) { + mDisplayPowerTimer = + std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds( + mSetDisplayPowerTimerMs), + [this] { resetDisplayPowerTimerCallback(); }, + [this] { + expiredDisplayPowerTimerCallback(); + }); + mDisplayPowerTimer->start(); + } } Scheduler::~Scheduler() { // Ensure the IdleTimer thread is joined before we start destroying state. + mDisplayPowerTimer.reset(); mTouchTimer.reset(); mIdleTimer.reset(); } sp<Scheduler::ConnectionHandle> Scheduler::createConnection( - const char* connectionName, int64_t phaseOffsetNs, ResyncCallback resyncCallback, + const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync, impl::EventThread::InterceptVSyncsCallback interceptCallback) { const int64_t id = sNextId++; ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id); std::unique_ptr<EventThread> eventThread = makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs, - std::move(interceptCallback)); + offsetThresholdForNextVsync, std::move(interceptCallback)); auto eventThreadConnection = - createConnectionInternal(eventThread.get(), std::move(resyncCallback)); + createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress); mConnections.emplace(id, std::make_unique<Connection>(new ConnectionHandle(id), eventThreadConnection, @@ -138,24 +151,26 @@ sp<Scheduler::ConnectionHandle> Scheduler::createConnection( } std::unique_ptr<EventThread> Scheduler::makeEventThread( - const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs, + const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs, + nsecs_t offsetThresholdForNextVsync, impl::EventThread::InterceptVSyncsCallback interceptCallback) { std::unique_ptr<VSyncSource> eventThreadSource = - std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, connectionName); + std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, offsetThresholdForNextVsync, + true, connectionName); return std::make_unique<impl::EventThread>(std::move(eventThreadSource), std::move(interceptCallback), connectionName); } -sp<EventThreadConnection> Scheduler::createConnectionInternal(EventThread* eventThread, - ResyncCallback&& resyncCallback) { - return eventThread->createEventConnection(std::move(resyncCallback)); +sp<EventThreadConnection> Scheduler::createConnectionInternal( + EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) { + return eventThread->createEventConnection([&] { resync(); }, configChanged); } sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection( - const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback) { + const sp<Scheduler::ConnectionHandle>& handle, + ISurfaceComposer::ConfigChanged configChanged) { RETURN_VALUE_IF_INVALID(nullptr); - return createConnectionInternal(mConnections[handle->id]->thread.get(), - std::move(resyncCallback)); + return createConnectionInternal(mConnections[handle->id]->thread.get(), configChanged); } EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) { @@ -245,23 +260,15 @@ void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) { setVsyncPeriod(period); } -ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) { - std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState; - return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() { - if (const auto vsync = ptr.lock()) { - vsync->resync(getVsyncPeriod); - } - }; -} - -void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) { - static constexpr nsecs_t kIgnoreDelay = ms2ns(500); +void Scheduler::resync() { + static constexpr nsecs_t kIgnoreDelay = ms2ns(750); const nsecs_t now = systemTime(); - const nsecs_t last = lastResyncTime.exchange(now); + const nsecs_t last = mLastResyncTime.exchange(now); if (now - last > kIgnoreDelay) { - scheduler.resyncToHardwareVsync(false, getVsyncPeriod()); + resyncToHardwareVsync(false, + mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod); } } @@ -280,13 +287,13 @@ void Scheduler::setVsyncPeriod(const nsecs_t period) { } } -void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodChanged) { +void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodFlushed) { bool needsHwVsync = false; - *periodChanged = false; + *periodFlushed = false; { // Scope for the lock std::lock_guard<std::mutex> lock(mHWVsyncLock); if (mPrimaryHWVsyncEnabled) { - needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodChanged); + needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodFlushed); } } @@ -309,7 +316,7 @@ void Scheduler::setIgnorePresentFences(bool ignore) { mPrimaryDispSync->setIgnorePresentFences(ignore); } -nsecs_t Scheduler::expectedPresentTime() { +nsecs_t Scheduler::getDispSyncExpectedPresentTime() { return mPrimaryDispSync->expectedPresentTime(); } @@ -319,13 +326,20 @@ void Scheduler::dumpPrimaryDispSync(std::string& result) const { std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer( std::string const& name, int windowType) { - RefreshRateType refreshRateType = (windowType == InputWindowInfo::TYPE_WALLPAPER) - ? RefreshRateType::DEFAULT - : RefreshRateType::PERFORMANCE; - - const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType); - const uint32_t fps = (refreshRate) ? refreshRate->fps : 0; - return mLayerHistory.createLayer(name, fps); + uint32_t defaultFps, performanceFps; + if (mRefreshRateConfigs.refreshRateSwitchingSupported()) { + defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps; + performanceFps = + mRefreshRateConfigs + .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER) + ? RefreshRateType::DEFAULT + : RefreshRateType::PERFORMANCE) + .fps; + } else { + defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps; + performanceFps = defaultFps; + } + return mLayerHistory.createLayer(name, defaultFps, performanceFps); } void Scheduler::addLayerPresentTimeAndHDR( @@ -371,16 +385,11 @@ void Scheduler::updateFpsBasedOnContent() { } void Scheduler::setChangeRefreshRateCallback( - const ChangeRefreshRateCallback& changeRefreshRateCallback) { + const ChangeRefreshRateCallback&& changeRefreshRateCallback) { std::lock_guard<std::mutex> lock(mCallbackLock); mChangeRefreshRateCallback = changeRefreshRateCallback; } -void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) { - std::lock_guard<std::mutex> lock(mCallbackLock); - mGetVsyncPeriod = getVsyncPeriod; -} - void Scheduler::updateFrameSkipping(const int64_t skipCount) { ATRACE_INT("FrameSkipCount", skipCount); if (mSkipCount != skipCount) { @@ -404,43 +413,77 @@ void Scheduler::notifyTouchEvent() { if (mSupportKernelTimer) { resetIdleTimer(); } + + // Touch event will boost the refresh rate to performance. + // Clear Layer History to get fresh FPS detection + mLayerHistory.clearHistory(); +} + +void Scheduler::setDisplayPowerState(bool normal) { + { + std::lock_guard<std::mutex> lock(mFeatureStateLock); + mIsDisplayPowerStateNormal = normal; + } + + if (mDisplayPowerTimer) { + mDisplayPowerTimer->reset(); + } + + // Display Power event will boost the refresh rate to performance. + // Clear Layer History to get fresh FPS detection + mLayerHistory.clearHistory(); } void Scheduler::resetTimerCallback() { - timerChangeRefreshRate(IdleTimerState::RESET); + handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::RESET, false); ATRACE_INT("ExpiredIdleTimer", 0); } void Scheduler::resetKernelTimerCallback() { ATRACE_INT("ExpiredKernelIdleTimer", 0); - std::lock_guard<std::mutex> lock(mCallbackLock); - if (mGetVsyncPeriod) { - resyncToHardwareVsync(false, mGetVsyncPeriod()); + const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate(); + if (refreshRate.first == RefreshRateType::PERFORMANCE) { + // If we're not in performance mode then the kernel timer shouldn't do + // anything, as the refresh rate during DPU power collapse will be the + // same. + resyncToHardwareVsync(true, refreshRate.second.vsyncPeriod); } } void Scheduler::expiredTimerCallback() { - timerChangeRefreshRate(IdleTimerState::EXPIRED); + handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::EXPIRED, false); ATRACE_INT("ExpiredIdleTimer", 1); } void Scheduler::resetTouchTimerCallback() { - // We do not notify the applications about config changes when idle timer is reset. - touchChangeRefreshRate(TouchState::ACTIVE); + handleTimerStateChanged(&mCurrentTouchState, TouchState::ACTIVE, true); ATRACE_INT("TouchState", 1); } void Scheduler::expiredTouchTimerCallback() { - // We do not notify the applications about config changes when idle timer expires. - touchChangeRefreshRate(TouchState::INACTIVE); + handleTimerStateChanged(&mCurrentTouchState, TouchState::INACTIVE, true); ATRACE_INT("TouchState", 0); } +void Scheduler::resetDisplayPowerTimerCallback() { + handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::RESET, true); + ATRACE_INT("ExpiredDisplayPowerTimer", 0); +} + +void Scheduler::expiredDisplayPowerTimerCallback() { + handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::EXPIRED, true); + ATRACE_INT("ExpiredDisplayPowerTimer", 1); +} + void Scheduler::expiredKernelTimerCallback() { ATRACE_INT("ExpiredKernelIdleTimer", 1); - // Disable HW Vsync if the timer expired, as we don't need it - // enabled if we're not pushing frames. - disableHardwareVsync(false); + const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate(); + if (refreshRate.first != RefreshRateType::PERFORMANCE) { + // Disable HW Vsync if the timer expired, as we don't need it + // enabled if we're not pushing frames, and if we're in PERFORMANCE + // mode then we'll need to re-update the DispSync model anyways. + disableHardwareVsync(false); + } } std::string Scheduler::doDump() { @@ -450,39 +493,23 @@ std::string Scheduler::doDump() { return stream.str(); } -void Scheduler::timerChangeRefreshRate(IdleTimerState idleTimerState) { - RefreshRateType newRefreshRateType; - { - std::lock_guard<std::mutex> lock(mFeatureStateLock); - if (mCurrentIdleTimerState == idleTimerState) { - return; - } - mCurrentIdleTimerState = idleTimerState; - newRefreshRateType = calculateRefreshRateType(); - if (mRefreshRateType == newRefreshRateType) { - return; - } - mRefreshRateType = newRefreshRateType; - } - changeRefreshRate(newRefreshRateType, ConfigEvent::None); -} - -void Scheduler::touchChangeRefreshRate(TouchState touchState) { +template <class T> +void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) { ConfigEvent event = ConfigEvent::None; RefreshRateType newRefreshRateType; { std::lock_guard<std::mutex> lock(mFeatureStateLock); - if (mCurrentTouchState == touchState) { + if (*currentState == newState) { return; } - mCurrentTouchState = touchState; + *currentState = newState; newRefreshRateType = calculateRefreshRateType(); if (mRefreshRateType == newRefreshRateType) { return; } mRefreshRateType = newRefreshRateType; - // Send an event in case that content detection is on as touch has a higher priority - if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) { + if (eventOnContentDetection && + mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) { event = ConfigEvent::Changed; } } @@ -490,11 +517,21 @@ void Scheduler::touchChangeRefreshRate(TouchState touchState) { } Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() { + if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) { + return RefreshRateType::DEFAULT; + } + // HDR content is not supported on PERFORMANCE mode if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) { return RefreshRateType::DEFAULT; } + // If Display Power is not in normal operation we want to be in performance mode. + // When coming back to normal mode, a grace period is given with DisplayPowerTimer + if (!mIsDisplayPowerStateNormal || mDisplayPowerTimerState == DisplayPowerTimerState::RESET) { + return RefreshRateType::PERFORMANCE; + } + // As long as touch is active we want to be in performance mode if (mCurrentTouchState == TouchState::ACTIVE) { return RefreshRateType::PERFORMANCE; @@ -510,27 +547,25 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() { return RefreshRateType::PERFORMANCE; } - // Content detection is on, find the appropriate refresh rate - // Start with the smallest refresh rate which is within a margin of the content - RefreshRateType currRefreshRateType = RefreshRateType::PERFORMANCE; - constexpr float MARGIN = 0.05f; - auto iter = mRefreshRateConfigs.getRefreshRates().cbegin(); - while (iter != mRefreshRateConfigs.getRefreshRates().cend()) { - if (iter->second->fps >= mContentRefreshRate * (1 - MARGIN)) { - currRefreshRateType = iter->first; - break; - } - ++iter; - } + // Content detection is on, find the appropriate refresh rate with minimal error + auto begin = mRefreshRateConfigs.getRefreshRateMap().cbegin(); + + auto iter = min_element(begin, mRefreshRateConfigs.getRefreshRateMap().cend(), + [rate = mContentRefreshRate](const auto& l, const auto& r) -> bool { + return std::abs(l.second.fps - static_cast<float>(rate)) < + std::abs(r.second.fps - static_cast<float>(rate)); + }); + RefreshRateType currRefreshRateType = iter->first; // Some content aligns better on higher refresh rate. For example for 45fps we should choose // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't // align well with both - float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps / + constexpr float MARGIN = 0.05f; + float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps / float(mContentRefreshRate); if (std::abs(std::round(ratio) - ratio) > MARGIN) { - while (iter != mRefreshRateConfigs.getRefreshRates().cend()) { - ratio = iter->second->fps / float(mContentRefreshRate); + while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) { + ratio = iter->second.fps / float(mContentRefreshRate); if (std::abs(std::round(ratio) - ratio) <= MARGIN) { currRefreshRateType = iter->first; @@ -543,6 +578,11 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() { return currRefreshRateType; } +Scheduler::RefreshRateType Scheduler::getPreferredRefreshRateType() { + std::lock_guard<std::mutex> lock(mFeatureStateLock); + return mRefreshRateType; +} + void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) { std::lock_guard<std::mutex> lock(mCallbackLock); if (mChangeRefreshRateCallback) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 96d4bd521..4b21dadde 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -50,7 +50,6 @@ public: using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType; using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>; - using GetVsyncPeriod = std::function<nsecs_t()>; // Enum to indicate whether to start the transaction early, or at vsync time. enum class TransactionStart { EARLY, NORMAL }; @@ -80,28 +79,18 @@ public: const std::unique_ptr<EventThread> thread; }; - // Stores per-display state about VSYNC. - struct VsyncState { - explicit VsyncState(Scheduler& scheduler) : scheduler(scheduler) {} - - void resync(const GetVsyncPeriod&); - - Scheduler& scheduler; - std::atomic<nsecs_t> lastResyncTime = 0; - }; - explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, const scheduler::RefreshRateConfigs& refreshRateConfig); virtual ~Scheduler(); /** Creates an EventThread connection. */ - sp<ConnectionHandle> createConnection(const char* connectionName, int64_t phaseOffsetNs, - ResyncCallback, + sp<ConnectionHandle> createConnection(const char* connectionName, nsecs_t phaseOffsetNs, + nsecs_t offsetThresholdForNextVsync, impl::EventThread::InterceptVSyncsCallback); - sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle, - ResyncCallback); + sp<IDisplayEventConnection> createDisplayEventConnection( + const sp<ConnectionHandle>& handle, ISurfaceComposer::ConfigChanged configChanged); // Getter methods. EventThread* getEventThread(const sp<ConnectionHandle>& handle); @@ -135,16 +124,20 @@ public: void enableHardwareVsync(); void disableHardwareVsync(bool makeUnavailable); + // Resyncs the scheduler to hardware vsync. + // If makeAvailable is true, then hardware vsync will be turned on. + // Otherwise, if hardware vsync is not already enabled then this method will + // no-op. + // The period is the vsync period from the current display configuration. void resyncToHardwareVsync(bool makeAvailable, nsecs_t period); - // Creates a callback for resyncing. - ResyncCallback makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod); + void resync(); void setRefreshSkipCount(int count); - // Passes a vsync sample to DispSync. periodChange will be true if DipSync - // detected that the vsync period changed, and false otherwise. - void addResyncSample(const nsecs_t timestamp, bool* periodChanged); + // Passes a vsync sample to DispSync. periodFlushed will be true if + // DispSync detected that the vsync period changed, and false otherwise. + void addResyncSample(const nsecs_t timestamp, bool* periodFlushed); void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime); void setIgnorePresentFences(bool ignore); - nsecs_t expectedPresentTime(); + nsecs_t getDispSyncExpectedPresentTime(); // Registers the layer in the scheduler, and returns the handle for future references. std::unique_ptr<scheduler::LayerHistory::LayerHandle> registerLayer(std::string const& name, int windowType); @@ -159,8 +152,7 @@ public: // Updates FPS based on the most content presented. void updateFpsBasedOnContent(); // Callback that gets invoked when Scheduler wants to change the refresh rate. - void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback); - void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod); + void setChangeRefreshRateCallback(const ChangeRefreshRateCallback&& changeRefreshRateCallback); // Returns whether idle timer is enabled or not bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; } @@ -171,15 +163,22 @@ public: // Function that resets the touch timer. void notifyTouchEvent(); + // Function that sets whether display power mode is normal or not. + void setDisplayPowerState(bool normal); + // Returns relevant information about Scheduler for dumpsys purposes. std::string doDump(); // calls DispSync::dump() on primary disp sync void dumpPrimaryDispSync(std::string& result) const; + // Get the appropriate refresh type for current conditions. + RefreshRateType getPreferredRefreshRateType(); + protected: virtual std::unique_ptr<EventThread> makeEventThread( - const char* connectionName, DispSync* dispSync, int64_t phaseOffsetNs, + const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs, + nsecs_t offsetThresholdForNextVsync, impl::EventThread::InterceptVSyncsCallback interceptCallback); private: @@ -190,9 +189,11 @@ private: enum class ContentFeatureState { CONTENT_DETECTION_ON, CONTENT_DETECTION_OFF }; enum class IdleTimerState { EXPIRED, RESET }; enum class TouchState { INACTIVE, ACTIVE }; + enum class DisplayPowerTimerState { EXPIRED, RESET }; // Creates a connection on the given EventThread and forwards the given callbacks. - sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&); + sp<EventThreadConnection> createConnectionInternal(EventThread*, + ISurfaceComposer::ConfigChanged); nsecs_t calculateAverage() const; void updateFrameSkipping(const int64_t skipCount); @@ -213,12 +214,15 @@ private: void resetTouchTimerCallback(); // Function that is called when the touch timer expires. void expiredTouchTimerCallback(); + // Function that is called when the display power timer resets. + void resetDisplayPowerTimerCallback(); + // Function that is called when the display power timer expires. + void expiredDisplayPowerTimerCallback(); // Sets vsync period. void setVsyncPeriod(const nsecs_t period); - // Idle timer feature's function to change the refresh rate. - void timerChangeRefreshRate(IdleTimerState idleTimerState); - // Touch timer feature's function to change the refresh rate. - void touchChangeRefreshRate(TouchState touchState); + // handles various timer features to change the refresh rate. + template <class T> + void handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection); // Calculate the new refresh rate type RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock); // Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters. @@ -243,7 +247,8 @@ private: std::mutex mHWVsyncLock; bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock); bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock); - const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)}; + + std::atomic<nsecs_t> mLastResyncTime = 0; std::unique_ptr<DispSync> mPrimaryDispSync; std::unique_ptr<EventControlThread> mEventControlThread; @@ -274,9 +279,12 @@ private: int64_t mSetTouchTimerMs = 0; std::unique_ptr<scheduler::IdleTimer> mTouchTimer; + // Timer used to monitor display power mode. + int64_t mSetDisplayPowerTimerMs = 0; + std::unique_ptr<scheduler::IdleTimer> mDisplayPowerTimer; + std::mutex mCallbackLock; ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock); - GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock); // In order to make sure that the features don't override themselves, we need a state machine // to keep track which feature requested the config change. @@ -285,14 +293,17 @@ private: ContentFeatureState::CONTENT_DETECTION_OFF; IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET; TouchState mCurrentTouchState GUARDED_BY(mFeatureStateLock) = TouchState::INACTIVE; + DisplayPowerTimerState mDisplayPowerTimerState GUARDED_BY(mFeatureStateLock) = + DisplayPowerTimerState::EXPIRED; uint32_t mContentRefreshRate GUARDED_BY(mFeatureStateLock); RefreshRateType mRefreshRateType GUARDED_BY(mFeatureStateLock); bool mIsHDRContent GUARDED_BY(mFeatureStateLock) = false; + bool mIsDisplayPowerStateNormal GUARDED_BY(mFeatureStateLock) = true; const scheduler::RefreshRateConfigs& mRefreshRateConfigs; // Global config to force HDR content to work on DEFAULT refreshRate - static constexpr bool mForceHDRContentToDefaultRefreshRate = true; + static constexpr bool mForceHDRContentToDefaultRefreshRate = false; }; } // namespace android diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h index 3bf3922ed..f193553ea 100644 --- a/services/surfaceflinger/Scheduler/SchedulerUtils.h +++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h @@ -30,19 +30,18 @@ using namespace std::chrono_literals; // about layers. static constexpr size_t ARRAY_SIZE = 30; -// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently -// the config is not visible to SF, and is completely maintained by HWC. However, we would -// still like to keep track of time when the device is in this config. -static constexpr int SCREEN_OFF_CONFIG_ID = -1; -static constexpr uint32_t HWC2_SCREEN_OFF_CONFIG_ID = 0xffffffff; - -// This number is used when we try to determine how long does a given layer stay relevant. -// Currently it is set to 100ms, because that would indicate 10Hz rendering. -static constexpr std::chrono::nanoseconds TIME_EPSILON_NS = 100ms; - // This number is used when we try to determine how long do we keep layer information around -// before we remove it. Currently it is set to 100ms. -static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 100ms; +// before we remove it. It is also used to determine how long the layer stays relevant. +// This time period captures infrequent updates when playing YouTube video with static image, +// or waiting idle in messaging app, when cursor is blinking. +static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 1200ms; + +// Layer is considered low activity if the LOW_ACTIVITY_BUFFERS buffers come more than +// LOW_ACTIVITY_EPSILON_NS apart. +// This is helping SF to vote for lower refresh rates when there is not activity +// in screen. +static constexpr int LOW_ACTIVITY_BUFFERS = 2; +static constexpr std::chrono::nanoseconds LOW_ACTIVITY_EPSILON_NS = 250ms; // Calculates the statistical mean (average) in the data structure (array, vector). The // function does not modify the contents of the array. diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp new file mode 100644 index 000000000..7a3bf8eda --- /dev/null +++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp @@ -0,0 +1,164 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "VSyncModulator.h" + +#include <cutils/properties.h> +#include <utils/Trace.h> + +#include <cinttypes> +#include <mutex> + +namespace android { + +using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType; +VSyncModulator::VSyncModulator() { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.vsync_trace_detailed_info", value, "0"); + mTraceDetailedInfo = atoi(value); + // Populate the offset map with some default offsets. + const Offsets defaultOffsets = {RefreshRateType::DEFAULT, 0, 0}; + setPhaseOffsets(defaultOffsets, defaultOffsets, defaultOffsets, 0); +} + +void VSyncModulator::setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late, + nsecs_t thresholdForNextVsync) { + std::lock_guard<std::mutex> lock(mMutex); + mOffsetMap.insert_or_assign(OffsetType::Early, early); + mOffsetMap.insert_or_assign(OffsetType::EarlyGl, earlyGl); + mOffsetMap.insert_or_assign(OffsetType::Late, late); + mThresholdForNextVsync = thresholdForNextVsync; + updateOffsetsLocked(); +} + +void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) { + if (transactionStart == Scheduler::TransactionStart::EARLY) { + mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION; + } + + // An early transaction stays an early transaction. + if (transactionStart == mTransactionStart || + mTransactionStart == Scheduler::TransactionStart::EARLY) { + return; + } + mTransactionStart = transactionStart; + updateOffsets(); +} + +void VSyncModulator::onTransactionHandled() { + if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return; + mTransactionStart = Scheduler::TransactionStart::NORMAL; + updateOffsets(); +} + +void VSyncModulator::onRefreshRateChangeInitiated() { + if (mRefreshRateChangePending) { + return; + } + mRefreshRateChangePending = true; + updateOffsets(); +} + +void VSyncModulator::onRefreshRateChangeCompleted() { + if (!mRefreshRateChangePending) { + return; + } + mRefreshRateChangePending = false; + updateOffsets(); +} + +void VSyncModulator::onRefreshed(bool usedRenderEngine) { + bool updateOffsetsNeeded = false; + if (mRemainingEarlyFrameCount > 0) { + mRemainingEarlyFrameCount--; + updateOffsetsNeeded = true; + } + if (usedRenderEngine) { + mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION; + updateOffsetsNeeded = true; + } else if (mRemainingRenderEngineUsageCount > 0) { + mRemainingRenderEngineUsageCount--; + updateOffsetsNeeded = true; + } + if (updateOffsetsNeeded) { + updateOffsets(); + } +} + +VSyncModulator::Offsets VSyncModulator::getOffsets() { + std::lock_guard<std::mutex> lock(mMutex); + return mOffsets; +} + +VSyncModulator::Offsets VSyncModulator::getNextOffsets() { + return mOffsetMap.at(getNextOffsetType()); +} + +VSyncModulator::OffsetType VSyncModulator::getNextOffsetType() { + // Early offsets are used if we're in the middle of a refresh rate + // change, or if we recently begin a transaction. + if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 || + mRefreshRateChangePending) { + return OffsetType::Early; + } else if (mRemainingRenderEngineUsageCount > 0) { + return OffsetType::EarlyGl; + } else { + return OffsetType::Late; + } +} + +void VSyncModulator::updateOffsets() { + std::lock_guard<std::mutex> lock(mMutex); + updateOffsetsLocked(); +} + +void VSyncModulator::updateOffsetsLocked() { + const Offsets desired = getNextOffsets(); + + if (mSfConnectionHandle != nullptr) { + mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf); + } + + if (mAppConnectionHandle != nullptr) { + mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app); + } + + flushOffsets(); +} + +void VSyncModulator::flushOffsets() { + OffsetType type = getNextOffsetType(); + mOffsets = mOffsetMap.at(type); + if (!mTraceDetailedInfo) { + return; + } + ATRACE_INT("Vsync-EarlyOffsetsOn", + mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Early); + ATRACE_INT("Vsync-EarlyGLOffsetsOn", + mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::EarlyGl); + ATRACE_INT("Vsync-LateOffsetsOn", + mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Late); + ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn", + mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Early); + ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn", + mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::EarlyGl); + ATRACE_INT("Vsync-HighFpsLateOffsetsOn", + mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Late); +} + +} // namespace android diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h index 81a7864cd..ddbd221ef 100644 --- a/services/surfaceflinger/Scheduler/VSyncModulator.h +++ b/services/surfaceflinger/Scheduler/VSyncModulator.h @@ -16,8 +16,6 @@ #pragma once -#include <utils/Errors.h> - #include <cinttypes> #include <mutex> @@ -35,12 +33,28 @@ private: // sending new transactions. const int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2; + // Number of frames we'll keep the early gl phase offsets once they are activated. + // This acts as a low-pass filter to avoid scenarios where we rapidly + // switch in and out of gl composition. + const int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2; + public: + VSyncModulator(); + + // Wrapper for a collection of surfaceflinger/app offsets for a particular + // configuration . struct Offsets { + scheduler::RefreshRateConfigs::RefreshRateType fpsMode; nsecs_t sf; nsecs_t app; }; + enum class OffsetType { + Early, + EarlyGl, + Late, + }; + // Sets the phase offsets // // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction @@ -51,31 +65,10 @@ public: // appEarly: Like sfEarly, but for the app-vsync // appEarlyGl: Like sfEarlyGl, but for the app-vsync. // appLate: The regular app vsync phase offset. - void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late) { - mEarlyOffsets = early; - mEarlyGlOffsets = earlyGl; - mLateOffsets = late; - - if (mSfConnectionHandle && late.sf != mOffsets.load().sf) { - mScheduler->setPhaseOffset(mSfConnectionHandle, late.sf); - } - - if (mAppConnectionHandle && late.app != mOffsets.load().app) { - mScheduler->setPhaseOffset(mAppConnectionHandle, late.app); - } - - mOffsets = late; - } - - Offsets getEarlyOffsets() const { return mEarlyOffsets; } - - Offsets getEarlyGlOffsets() const { return mEarlyGlOffsets; } - - void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) { - mSfEventThread = sfEventThread; - mAppEventThread = appEventThread; - } + void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late, + nsecs_t thresholdForNextVsync) EXCLUDES(mMutex); + // Sets the scheduler and vsync connection handlers. void setSchedulerAndHandles(Scheduler* scheduler, Scheduler::ConnectionHandle* appConnectionHandle, Scheduler::ConnectionHandle* sfConnectionHandle) { @@ -84,120 +77,57 @@ public: mSfConnectionHandle = sfConnectionHandle; } - void setTransactionStart(Scheduler::TransactionStart transactionStart) { - if (transactionStart == Scheduler::TransactionStart::EARLY) { - mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION; - } - - // An early transaction stays an early transaction. - if (transactionStart == mTransactionStart || - mTransactionStart == Scheduler::TransactionStart::EARLY) { - return; - } - mTransactionStart = transactionStart; - updateOffsets(); - } + // Signals that a transaction has started, and changes offsets accordingly. + void setTransactionStart(Scheduler::TransactionStart transactionStart); - void onTransactionHandled() { - if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return; - mTransactionStart = Scheduler::TransactionStart::NORMAL; - updateOffsets(); - } + // Signals that a transaction has been completed, so that we can finish + // special handling for a transaction. + void onTransactionHandled(); // Called when we send a refresh rate change to hardware composer, so that // we can move into early offsets. - void onRefreshRateChangeInitiated() { - if (mRefreshRateChangePending) { - return; - } - mRefreshRateChangePending = true; - updateOffsets(); - } + void onRefreshRateChangeInitiated(); // Called when we detect from vsync signals that the refresh rate changed. // This way we can move out of early offsets if no longer necessary. - void onRefreshRateChangeDetected() { - if (!mRefreshRateChangePending) { - return; - } - mRefreshRateChangePending = false; - updateOffsets(); - } + void onRefreshRateChangeCompleted(); - void onRefreshed(bool usedRenderEngine) { - bool updateOffsetsNeeded = false; - if (mRemainingEarlyFrameCount > 0) { - mRemainingEarlyFrameCount--; - updateOffsetsNeeded = true; - } - if (usedRenderEngine != mLastFrameUsedRenderEngine) { - mLastFrameUsedRenderEngine = usedRenderEngine; - updateOffsetsNeeded = true; - } - if (updateOffsetsNeeded) { - updateOffsets(); - } - } + // Called when the display is presenting a new frame. usedRenderEngine + // should be set to true if RenderEngine was involved with composing the new + // frame. + void onRefreshed(bool usedRenderEngine); - Offsets getOffsets() { - // Early offsets are used if we're in the middle of a refresh rate - // change, or if we recently begin a transaction. - if (mTransactionStart == Scheduler::TransactionStart::EARLY || - mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) { - return mEarlyOffsets; - } else if (mLastFrameUsedRenderEngine) { - return mEarlyGlOffsets; - } else { - return mLateOffsets; - } - } + // Returns the offsets that we are currently using + Offsets getOffsets() EXCLUDES(mMutex); private: - void updateOffsets() { - const Offsets desired = getOffsets(); - const Offsets current = mOffsets; - - bool changed = false; - if (desired.sf != current.sf) { - if (mSfConnectionHandle != nullptr) { - mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf); - } else { - mSfEventThread->setPhaseOffset(desired.sf); - } - changed = true; - } - if (desired.app != current.app) { - if (mAppConnectionHandle != nullptr) { - mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app); - } else { - mAppEventThread->setPhaseOffset(desired.app); - } - changed = true; - } - - if (changed) { - mOffsets = desired; - } - } - - Offsets mLateOffsets; - Offsets mEarlyOffsets; - Offsets mEarlyGlOffsets; - - EventThread* mSfEventThread = nullptr; - EventThread* mAppEventThread = nullptr; + // Returns the next offsets that we should be using + Offsets getNextOffsets() REQUIRES(mMutex); + // Returns the next offset type that we should use. + OffsetType getNextOffsetType(); + // Updates offsets and persists them into the scheduler framework. + void updateOffsets() EXCLUDES(mMutex); + void updateOffsetsLocked() REQUIRES(mMutex); + // Updates the internal offsets and offset type. + void flushOffsets() REQUIRES(mMutex); + + mutable std::mutex mMutex; + std::unordered_map<OffsetType, Offsets> mOffsetMap GUARDED_BY(mMutex); + nsecs_t mThresholdForNextVsync; Scheduler* mScheduler = nullptr; Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr; Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr; - std::atomic<Offsets> mOffsets; + Offsets mOffsets GUARDED_BY(mMutex) = {Scheduler::RefreshRateType::DEFAULT, 0, 0}; std::atomic<Scheduler::TransactionStart> mTransactionStart = Scheduler::TransactionStart::NORMAL; - std::atomic<bool> mLastFrameUsedRenderEngine = false; std::atomic<bool> mRefreshRateChangePending = false; std::atomic<int> mRemainingEarlyFrameCount = 0; + std::atomic<int> mRemainingRenderEngineUsageCount = 0; + + bool mTraceDetailedInfo = false; }; } // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 6166789fc..fd7f12841 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -48,6 +48,8 @@ #include <compositionengine/impl/OutputLayerCompositionState.h> #include <dvr/vr_flinger.h> #include <gui/BufferQueue.h> +#include <gui/DebugEGLImageTracker.h> + #include <gui/GuiConfig.h> #include <gui/IDisplayEventConnection.h> #include <gui/IProducerListener.h> @@ -311,6 +313,9 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI wideColorGamutCompositionPixelFormat = static_cast<ui::PixelFormat>(wcg_composition_pixel_format(ui::PixelFormat::RGBA_8888)); + mColorSpaceAgnosticDataspace = + static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN)); + useContextPriority = use_context_priority(true); auto tmpPrimaryDisplayOrientation = primary_display_orientation( @@ -382,7 +387,8 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI mLumaSampling = atoi(value); const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); - mVsyncModulator.setPhaseOffsets(early, gl, late); + mVsyncModulator.setPhaseOffsets(early, gl, late, + mPhaseOffsets->getOffsetThresholdForNextVsync()); // We should be reading 'persist.sys.sf.color_saturation' here // but since /data may be encrypted, we need to wait until after vold @@ -563,14 +569,16 @@ void SurfaceFlinger::bootFinished() readPersistentProperties(); mBootStage = BootStage::FINISHED; - // set the refresh rate according to the policy - const auto& performanceRefreshRate = - mRefreshRateConfigs.getRefreshRate(RefreshRateType::PERFORMANCE); + if (mRefreshRateConfigs->refreshRateSwitchingSupported()) { + // set the refresh rate according to the policy + const auto& performanceRefreshRate = + mRefreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE); - if (performanceRefreshRate && isDisplayConfigAllowed(performanceRefreshRate->configId)) { - setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None); - } else { - setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None); + if (isDisplayConfigAllowed(performanceRefreshRate.configId)) { + setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None); + } else { + setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None); + } } })); } @@ -613,29 +621,6 @@ void SurfaceFlinger::init() { ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset()); Mutex::Autolock _l(mStateLock); - // start the EventThread - mScheduler = - getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); }, - mRefreshRateConfigs); - auto resyncCallback = - mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this)); - - mAppConnectionHandle = - mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(), - resyncCallback, - impl::EventThread::InterceptVSyncsCallback()); - mSfConnectionHandle = mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(), - resyncCallback, [this](nsecs_t timestamp) { - mInterceptor->saveVSyncEvent(timestamp); - }); - - mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle)); - mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(), - mSfConnectionHandle.get()); - - mRegionSamplingThread = - new RegionSamplingThread(*this, *mScheduler, - RegionSamplingThread::EnvironmentTimingTunables()); // Get a RenderEngine for the given display / config (can't fail) int32_t renderEngineFeature = 0; @@ -706,19 +691,6 @@ void SurfaceFlinger::init() { ALOGE("Run StartPropertySetThread failed!"); } - mScheduler->setChangeRefreshRateCallback( - [this](RefreshRateType type, Scheduler::ConfigEvent event) { - Mutex::Autolock lock(mStateLock); - setRefreshRateTo(type, event); - }); - mScheduler->setGetVsyncPeriodCallback([this] { - Mutex::Autolock lock(mStateLock); - return getVsyncPeriod(); - }); - - mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId())); - mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId())); - ALOGV("Done initializing"); } @@ -872,7 +844,8 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken, info.xdpi = xdpi; info.ydpi = ydpi; info.fps = 1e9 / hwConfig->getVsyncPeriod(); - const auto refreshRateType = mRefreshRateConfigs.getRefreshRateType(hwConfig->getId()); + const auto refreshRateType = + mRefreshRateConfigs->getRefreshRateTypeFromHwcConfigId(hwConfig->getId()); const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType); info.appVsyncOffset = offset.late.app; @@ -940,10 +913,13 @@ void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) { // Start receiving vsync samples now, so that we can detect a period // switch. mScheduler->resyncToHardwareVsync(true, getVsyncPeriod()); + // As we called to set period, we will call to onRefreshRateChangeCompleted once + // DispSync model is locked. + mVsyncModulator.onRefreshRateChangeInitiated(); mPhaseOffsets->setRefreshRateType(info.type); const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); - mVsyncModulator.onRefreshRateChangeInitiated(); - mVsyncModulator.setPhaseOffsets(early, gl, late); + mVsyncModulator.setPhaseOffsets(early, gl, late, + mPhaseOffsets->getOffsetThresholdForNextVsync()); } mDesiredActiveConfigChanged = true; ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged); @@ -971,14 +947,15 @@ void SurfaceFlinger::setActiveConfigInternal() { } std::lock_guard<std::mutex> lock(mActiveConfigLock); - mRefreshRateStats.setConfigMode(mUpcomingActiveConfig.configId); + mRefreshRateConfigs->setCurrentConfig(mUpcomingActiveConfig.configId); + mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId); display->setActiveConfig(mUpcomingActiveConfig.configId); - mScheduler->resyncToHardwareVsync(true, getVsyncPeriod()); mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type); const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); - mVsyncModulator.setPhaseOffsets(early, gl, late); + mVsyncModulator.setPhaseOffsets(early, gl, late, + mPhaseOffsets->getOffsetThresholdForNextVsync()); ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId); if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) { @@ -987,6 +964,19 @@ void SurfaceFlinger::setActiveConfigInternal() { } } +void SurfaceFlinger::desiredActiveConfigChangeDone() { + std::lock_guard<std::mutex> lock(mActiveConfigLock); + mDesiredActiveConfig.event = Scheduler::ConfigEvent::None; + mDesiredActiveConfigChanged = false; + ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged); + + mScheduler->resyncToHardwareVsync(true, getVsyncPeriod()); + mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type); + const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); + mVsyncModulator.setPhaseOffsets(early, gl, late, + mPhaseOffsets->getOffsetThresholdForNextVsync()); +} + bool SurfaceFlinger::performSetActiveConfig() { ATRACE_CALL(); if (mCheckPendingFence) { @@ -1016,10 +1006,7 @@ bool SurfaceFlinger::performSetActiveConfig() { if (!display || display->getActiveConfig() == desiredActiveConfig.configId) { // display is not valid or we are already in the requested mode // on both cases there is nothing left to do - std::lock_guard<std::mutex> lock(mActiveConfigLock); - mDesiredActiveConfig.event = Scheduler::ConfigEvent::None; - mDesiredActiveConfigChanged = false; - ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged); + desiredActiveConfigChangeDone(); return false; } @@ -1027,13 +1014,10 @@ bool SurfaceFlinger::performSetActiveConfig() { // allowed configs might have change by the time we process the refresh. // Make sure the desired config is still allowed if (!isDisplayConfigAllowed(desiredActiveConfig.configId)) { - std::lock_guard<std::mutex> lock(mActiveConfigLock); - mDesiredActiveConfig.event = Scheduler::ConfigEvent::None; - mDesiredActiveConfig.configId = display->getActiveConfig(); - mDesiredActiveConfigChanged = false; - ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged); + desiredActiveConfigChangeDone(); return false; } + mUpcomingActiveConfig = desiredActiveConfig; const auto displayId = display->getId(); LOG_ALWAYS_FATAL_IF(!displayId); @@ -1243,9 +1227,6 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { return; } - auto resyncCallback = - mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this)); - // TODO(b/128863962): Part of the Injector should be refactored, so that it // can be passed to Scheduler. if (enable) { @@ -1257,11 +1238,11 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { impl::EventThread::InterceptVSyncsCallback(), "injEventThread"); } - mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback)); + mEventQueue->setEventThread(mInjectorEventThread.get(), [&] { mScheduler->resync(); }); } else { ALOGV("VSync Injections disabled"); mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle), - std::move(resyncCallback)); + [&] { mScheduler->resync(); }); } mInjectVSyncs = enable; @@ -1371,16 +1352,11 @@ status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) { // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( - ISurfaceComposer::VsyncSource vsyncSource) { - auto resyncCallback = mScheduler->makeResyncCallback([this] { - Mutex::Autolock lock(mStateLock); - return getVsyncPeriod(); - }); - + ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) { const auto& handle = vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle; - return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback)); + return mScheduler->createDisplayEventConnection(handle, configChanged); } // ---------------------------------------------------------------------------- @@ -1453,10 +1429,10 @@ void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDispl return; } - bool periodChanged = false; - mScheduler->addResyncSample(timestamp, &periodChanged); - if (periodChanged) { - mVsyncModulator.onRefreshRateChangeDetected(); + bool periodFlushed = false; + mScheduler->addResyncSample(timestamp, &periodFlushed); + if (periodFlushed) { + mVsyncModulator.onRefreshRateChangeCompleted(); } } @@ -1477,13 +1453,8 @@ void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::Co ATRACE_CALL(); // Don't do any updating if the current fps is the same as the new one. - const auto& refreshRateConfig = mRefreshRateConfigs.getRefreshRate(refreshRate); - if (!refreshRateConfig) { - ALOGV("Skipping refresh rate change request for unsupported rate."); - return; - } - - const int desiredConfigId = refreshRateConfig->configId; + const auto& refreshRateConfig = mRefreshRateConfigs->getRefreshRateFromType(refreshRate); + const int desiredConfigId = refreshRateConfig.configId; if (!isDisplayConfigAllowed(desiredConfigId)) { ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId); @@ -1529,10 +1500,23 @@ void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDispl void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) { ATRACE_CALL(); - Mutex::Autolock lock(mStateLock); + + // Enable / Disable HWVsync from the main thread to avoid race conditions with + // display power state. + postMessageAsync(new LambdaMessage( + [=]() NO_THREAD_SAFETY_ANALYSIS { setPrimaryVsyncEnabledInternal(enabled); })); +} + +void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) { + ATRACE_CALL(); + + mHWCVsyncPendingState = enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable; + if (const auto displayId = getInternalDisplayIdLocked()) { - getHwComposer().setVsyncEnabled(*displayId, - enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable); + sp<DisplayDevice> display = getDefaultDisplayDeviceLocked(); + if (display && display->isPoweredOn()) { + setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState); + } } } @@ -1630,7 +1614,8 @@ void SurfaceFlinger::updateVrFlinger() { setTransactionFlags(eDisplayTransactionNeeded); } -bool SurfaceFlinger::previousFrameMissed() NO_THREAD_SAFETY_ANALYSIS { +bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS { + ATRACE_CALL(); // We are storing the last 2 present fences. If sf's phase offset is to be // woken up before the actual vsync but targeting the next vsync, we need to check // fence N-2 @@ -1639,14 +1624,46 @@ bool SurfaceFlinger::previousFrameMissed() NO_THREAD_SAFETY_ANALYSIS { ? mPreviousPresentFences[0] : mPreviousPresentFences[1]; - return fence != Fence::NO_FENCE && (fence->getStatus() == Fence::Status::Unsignaled); + if (fence == Fence::NO_FENCE) { + return false; + } + + if (graceTimeMs > 0 && fence->getStatus() == Fence::Status::Unsignaled) { + fence->wait(graceTimeMs); + } + + return (fence->getStatus() == Fence::Status::Unsignaled); +} + +void SurfaceFlinger::populateExpectedPresentTime() NO_THREAD_SAFETY_ANALYSIS { + DisplayStatInfo stats; + mScheduler->getDisplayStatInfo(&stats); + const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(); + // Inflate the expected present time if we're targetting the next vsync. + mExpectedPresentTime = + mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync() + ? presentTime + : presentTime + stats.vsyncPeriod; } void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS { ATRACE_CALL(); switch (what) { case MessageQueue::INVALIDATE: { - bool frameMissed = previousFrameMissed(); + // calculate the expected present time once and use the cached + // value throughout this frame to make sure all layers are + // seeing this same value. + populateExpectedPresentTime(); + + // When Backpressure propagation is enabled we want to give a small grace period + // for the present fence to fire instead of just giving up on this frame to handle cases + // where present fence is just about to get signaled. + const int graceTimeForPresentFenceMs = + (mPropagateBackpressure && + (mPropagateBackpressureClientComposition || !mHadClientComposition)) + ? 1 + : 0; + bool frameMissed = previousFrameMissed(graceTimeForPresentFenceMs); bool hwcFrameMissed = mHadDeviceComposition && frameMissed; bool gpuFrameMissed = mHadClientComposition && frameMissed; ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); @@ -1845,7 +1862,14 @@ void SurfaceFlinger::calculateWorkingSet() { RenderIntent renderIntent; pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent); display->setColorMode(colorMode, targetDataspace, renderIntent); + + if (isHdrColorMode(colorMode)) { + targetDataspace = Dataspace::UNKNOWN; + } else if (mColorSpaceAgnosticDataspace != Dataspace::UNKNOWN) { + targetDataspace = mColorSpaceAgnosticDataspace; + } } + for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { if (layer->isHdrY410()) { layer->forceClientComposition(displayDevice); @@ -1872,9 +1896,7 @@ void SurfaceFlinger::calculateWorkingSet() { const auto& displayState = display->getState(); layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport, - displayDevice->getSupportedPerFrameMetadata(), - isHdrColorMode(displayState.colorMode) ? Dataspace::UNKNOWN - : targetDataspace); + displayDevice->getSupportedPerFrameMetadata(), targetDataspace); } } @@ -2537,6 +2559,9 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { if (event.connection == HWC2::Connection::Connected) { if (!mPhysicalDisplayTokens.count(info->id)) { ALOGV("Creating display %s", to_string(info->id).c_str()); + if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) { + initScheduler(info->id); + } mPhysicalDisplayTokens[info->id] = new BBinder(); DisplayDeviceState state; state.displayId = info->id; @@ -2937,7 +2962,7 @@ void SurfaceFlinger::updateInputFlinger() { setInputWindowsFinished(); } - executeInputWindowCommands(); + mInputWindowCommands.clear(); } void SurfaceFlinger::updateInputWindowInfo() { @@ -2961,19 +2986,6 @@ void SurfaceFlinger::commitInputWindowCommands() { mPendingInputWindowCommands.clear(); } -void SurfaceFlinger::executeInputWindowCommands() { - for (const auto& transferTouchFocusCommand : mInputWindowCommands.transferTouchFocusCommands) { - if (transferTouchFocusCommand.fromToken != nullptr && - transferTouchFocusCommand.toToken != nullptr && - transferTouchFocusCommand.fromToken != transferTouchFocusCommand.toToken) { - mInputFlinger->transferTouchFocus(transferTouchFocusCommand.fromToken, - transferTouchFocusCommand.toToken); - } - } - - mInputWindowCommands.clear(); -} - void SurfaceFlinger::updateCursorAsync() { for (const auto& [token, display] : mDisplays) { @@ -2995,6 +3007,55 @@ void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) { layer->releasePendingBuffer(systemTime()); } +void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) { + if (mScheduler) { + // In practice it's not allowed to hotplug in/out the primary display once it's been + // connected during startup, but some tests do it, so just warn and return. + ALOGW("Can't re-init scheduler"); + return; + } + + int currentConfig = getHwComposer().getActiveConfigIndex(primaryDisplayId); + mRefreshRateConfigs = + std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false), + getHwComposer().getConfigs( + primaryDisplayId), + currentConfig); + mRefreshRateStats = + std::make_unique<scheduler::RefreshRateStats>(*mRefreshRateConfigs, *mTimeStats, + currentConfig, HWC_POWER_MODE_OFF); + mRefreshRateStats->setConfigMode(currentConfig); + + // start the EventThread + mScheduler = + getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); }, + *mRefreshRateConfigs); + mAppConnectionHandle = + mScheduler->createConnection("app", mVsyncModulator.getOffsets().app, + mPhaseOffsets->getOffsetThresholdForNextVsync(), + impl::EventThread::InterceptVSyncsCallback()); + mSfConnectionHandle = + mScheduler->createConnection("sf", mVsyncModulator.getOffsets().sf, + mPhaseOffsets->getOffsetThresholdForNextVsync(), + [this](nsecs_t timestamp) { + mInterceptor->saveVSyncEvent(timestamp); + }); + + mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle)); + mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(), + mSfConnectionHandle.get()); + + mRegionSamplingThread = + new RegionSamplingThread(*this, *mScheduler, + RegionSamplingThread::EnvironmentTimingTunables()); + + mScheduler->setChangeRefreshRateCallback( + [this](RefreshRateType type, Scheduler::ConfigEvent event) { + Mutex::Autolock lock(mStateLock); + setRefreshRateTo(type, event); + }); +} + void SurfaceFlinger::commitTransaction() { if (!mLayersPendingRemoval.isEmpty()) { @@ -3250,8 +3311,7 @@ bool SurfaceFlinger::handlePageFlip() mDrawingState.traverseInZOrder([&](Layer* layer) { if (layer->hasReadyFrame()) { frameQueued = true; - nsecs_t expectedPresentTime; - expectedPresentTime = mScheduler->expectedPresentTime(); + const nsecs_t expectedPresentTime = getExpectedPresentTime(); if (layer->shouldPresentNow(expectedPresentTime)) { mLayersWithQueuedFrames.push_back(layer); } else { @@ -3648,7 +3708,7 @@ bool SurfaceFlinger::containsAnyInvalidClientState(const Vector<ComposerState>& bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime, const Vector<ComposerState>& states) { - nsecs_t expectedPresentTime = mScheduler->expectedPresentTime(); + nsecs_t expectedPresentTime = getExpectedPresentTime(); // Do not present if the desiredPresentTime has not passed unless it is more than one second // in the future. We ignore timestamps more than 1 second in the future for stability reasons. if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime && @@ -3772,6 +3832,7 @@ void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states, if (uncacheBuffer.isValid()) { ClientCache::getInstance().erase(uncacheBuffer); + getRenderEngine().unbindExternalTextureBuffer(uncacheBuffer.id); } // If a synchronous transaction is explicitly requested without any changes, force a transaction @@ -4126,9 +4187,18 @@ uint32_t SurfaceFlinger::setClientStateLocked( bool bufferChanged = what & layer_state_t::eBufferChanged; bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged; sp<GraphicBuffer> buffer; - if (bufferChanged && cacheIdChanged) { - ClientCache::getInstance().add(s.cachedBuffer, s.buffer); + if (bufferChanged && cacheIdChanged && s.buffer != nullptr) { buffer = s.buffer; + bool success = ClientCache::getInstance().add(s.cachedBuffer, s.buffer); + if (success) { + getRenderEngine().cacheExternalTextureBuffer(s.buffer); + success = ClientCache::getInstance() + .registerErasedRecipient(s.cachedBuffer, + wp<ClientCache::ErasedRecipient>(this)); + if (!success) { + getRenderEngine().unbindExternalTextureBuffer(s.buffer->getId()); + } + } } else if (cacheIdChanged) { buffer = ClientCache::getInstance().get(s.cachedBuffer); } else if (bufferChanged) { @@ -4411,6 +4481,13 @@ void SurfaceFlinger::initializeDisplays() { new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); })); } +void SurfaceFlinger::setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled) { + if (mHWCVsyncState != enabled) { + getHwComposer().setVsyncEnabled(displayId, enabled); + mHWCVsyncState = enabled; + } +} + void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mode) { if (display->isVirtual()) { ALOGE("%s: Invalid operation on virtual display", __FUNCTION__); @@ -4437,6 +4514,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int // Turn on the display getHwComposer().setPowerMode(*displayId, mode); if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) { + setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState); mScheduler->onScreenAcquired(mAppConnectionHandle); mScheduler->resyncToHardwareVsync(true, getVsyncPeriod()); } @@ -4462,6 +4540,9 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mScheduler->onScreenReleased(mAppConnectionHandle); } + // Make sure HWVsync is disabled before turning off the display + setVsyncEnabledInHWC(*displayId, HWC2::Vsync::Disable); + getHwComposer().setPowerMode(*displayId, mode); mVisibleRegionsDirty = true; // from this point on, SF will stop drawing on this display @@ -4487,7 +4568,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int if (display->isPrimary()) { mTimeStats->setPowerMode(mode); - mRefreshRateStats.setPowerMode(mode); + mRefreshRateStats->setPowerMode(mode); + mScheduler->setDisplayPowerState(mode == HWC_POWER_MODE_NORMAL); } ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str()); @@ -4652,7 +4734,16 @@ void SurfaceFlinger::dumpVSync(std::string& result) const { StringAppendF(&result, "Scheduler enabled."); StringAppendF(&result, "+ Smart 90 for video detection: %s\n\n", mUseSmart90ForVideo ? "on" : "off"); + StringAppendF(&result, "Allowed Display Configs: "); + for (int32_t configId : mAllowedDisplayConfigs) { + StringAppendF(&result, "%" PRIu32 " Hz, ", + mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps); + } + StringAppendF(&result, "(config override by backdoor: %s)\n\n", + mDebugDisplayConfigSetByBackdoor ? "yes" : "no"); mScheduler->dump(mAppConnectionHandle, result); + StringAppendF(&result, "+ Refresh rate switching: %s\n", + mRefreshRateConfigs->refreshRateSwitchingSupported() ? "on" : "off"); } void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const { @@ -4937,6 +5028,8 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co getRenderEngine().dump(result); + DebugEGLImageTracker::getInstance()->dump(result); + if (const auto display = getDefaultDisplayDeviceLocked()) { display->getCompositionDisplay()->getState().undefinedRegion.dump(result, "undefinedRegion"); @@ -5015,7 +5108,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append("\nScheduler state:\n"); result.append(mScheduler->doDump() + "\n"); StringAppendF(&result, "+ Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off"); - result.append(mRefreshRateStats.doDump() + "\n"); + result.append(mRefreshRateStats->doDump() + "\n"); result.append(mTimeStats->miniDump()); result.append("\n"); @@ -5350,7 +5443,12 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r return NO_ERROR; } case 1023: { // Set native mode + int32_t colorMode; + mDisplayColorSetting = static_cast<DisplayColorSetting>(data.readInt32()); + if (data.readInt32(&colorMode) == NO_ERROR) { + mForceColorMode = static_cast<ColorMode>(colorMode); + } invalidateHwcGeometry(); repaintEverything(); return NO_ERROR; @@ -5481,7 +5579,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r case 1034: { // TODO(b/129297325): expose this via developer menu option n = data.readInt32(); - if (n && !mRefreshRateOverlay) { + if (n && !mRefreshRateOverlay && + mRefreshRateConfigs->refreshRateSwitchingSupported()) { RefreshRateType type; { std::lock_guard<std::mutex> lock(mActiveConfigLock); @@ -6065,18 +6164,43 @@ void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& d return; } + const auto allowedDisplayConfigs = DisplayConfigs(allowedConfigs.begin(), + allowedConfigs.end()); + if (allowedDisplayConfigs == mAllowedDisplayConfigs) { + return; + } + ALOGV("Updating allowed configs"); - mAllowedDisplayConfigs = DisplayConfigs(allowedConfigs.begin(), allowedConfigs.end()); - - // Set the highest allowed config by iterating backwards on available refresh rates - const auto& refreshRates = mRefreshRateConfigs.getRefreshRates(); - for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) { - if (iter->second && isDisplayConfigAllowed(iter->second->configId)) { - ALOGV("switching to config %d", iter->second->configId); - setDesiredActiveConfig( - {iter->first, iter->second->configId, Scheduler::ConfigEvent::Changed}); - break; + mAllowedDisplayConfigs = std::move(allowedDisplayConfigs); + + // TODO(b/140204874): This hack triggers a notification that something has changed, so + // that listeners that care about a change in allowed configs can get the notification. + // Giving current ActiveConfig so that most other listeners would just drop the event + mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, + display->getActiveConfig()); + + if (mRefreshRateConfigs->refreshRateSwitchingSupported()) { + const auto& type = mScheduler->getPreferredRefreshRateType(); + const auto& config = mRefreshRateConfigs->getRefreshRateFromType(type); + if (isDisplayConfigAllowed(config.configId)) { + ALOGV("switching to Scheduler preferred config %d", config.configId); + setDesiredActiveConfig({type, config.configId, Scheduler::ConfigEvent::Changed}); + } else { + // Set the highest allowed config by iterating backwards on available refresh rates + const auto& refreshRates = mRefreshRateConfigs->getRefreshRateMap(); + for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) { + if (isDisplayConfigAllowed(iter->second.configId)) { + ALOGV("switching to allowed config %d", iter->second.configId); + setDesiredActiveConfig( + {iter->first, iter->second.configId, Scheduler::ConfigEvent::Changed}); + break; + } + } } + } else if (!isDisplayConfigAllowed(display->getActiveConfig())) { + ALOGV("switching to config %d", allowedConfigs[0]); + setDesiredActiveConfig( + {RefreshRateType::DEFAULT, allowedConfigs[0], Scheduler::ConfigEvent::Changed}); } } @@ -6093,7 +6217,7 @@ status_t SurfaceFlinger::setAllowedDisplayConfigs(const sp<IBinder>& displayToke return NO_ERROR; } - postMessageSync(new LambdaMessage([&]() NO_THREAD_SAFETY_ANALYSIS { + postMessageSync(new LambdaMessage([&]() { const auto display = getDisplayDeviceLocked(displayToken); if (!display) { ALOGE("Attempt to set allowed display configs for invalid display token %p", @@ -6101,6 +6225,7 @@ status_t SurfaceFlinger::setAllowedDisplayConfigs(const sp<IBinder>& displayToke } else if (display->isVirtual()) { ALOGW("Attempt to set allowed display configs for virtual display"); } else { + Mutex::Autolock lock(mStateLock); setAllowedDisplayConfigsInternal(display, allowedConfigs); } })); @@ -6146,6 +6271,10 @@ sp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) { return nullptr; } +void SurfaceFlinger::bufferErased(const client_cache_t& clientCacheId) { + getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id); +} + } // namespace android #if defined(__gl_h_) diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 52655944d..a22d6fc64 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -170,9 +170,9 @@ public: class SurfaceFlinger : public BnSurfaceComposer, public PriorityDumper, + public ClientCache::ErasedRecipient, private IBinder::DeathRecipient, - private HWC2::ComposerCallback -{ + private HWC2::ComposerCallback { public: SurfaceFlingerBE& getBE() { return mBE; } const SurfaceFlingerBE& getBE() const { return mBE; } @@ -293,11 +293,20 @@ public: // TODO: this should be made accessible only to EventThread void setPrimaryVsyncEnabled(bool enabled); + // main thread function to enable/disable h/w composer event + void setPrimaryVsyncEnabledInternal(bool enabled); + // called on the main thread by MessageQueue when an internal message // is received // TODO: this should be made accessible only to MessageQueue void onMessageReceived(int32_t what); + // populates the expected present time for this frame. + // When we are in negative offsets, we perform a correction so that the + // predicted vsync for the *next* frame is used instead. + void populateExpectedPresentTime(); + nsecs_t getExpectedPresentTime() const { return mExpectedPresentTime; } + // for debugging only // TODO: this should be made accessible only to HWComposer const Vector<sp<Layer>>& getLayerSortedByZForHwcDisplay(DisplayId displayId); @@ -319,6 +328,9 @@ public: sp<Layer> fromHandle(const sp<IBinder>& handle) REQUIRES(mStateLock); + // Inherit from ClientCache::ErasedRecipient + void bufferErased(const client_cache_t& clientCacheId) override; + private: friend class BufferLayer; friend class BufferQueueLayer; @@ -399,7 +411,9 @@ private: const sp<IGraphicBufferProducer>& bufferProducer) const override; status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override; sp<IDisplayEventConnection> createDisplayEventConnection( - ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp) override; + ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp, + ISurfaceComposer::ConfigChanged configChanged = + ISurfaceComposer::eConfigChangedSuppress) override; status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, Rect sourceCrop, @@ -508,13 +522,15 @@ private: // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig. void setDesiredActiveConfig(const ActiveConfigInfo& info) REQUIRES(mStateLock); // Once HWC has returned the present fence, this sets the active config and a new refresh - // rate in SF. It also triggers HWC vsync. + // rate in SF. void setActiveConfigInternal() REQUIRES(mStateLock); // Active config is updated on INVALIDATE call in a state machine-like manner. When the // desired config was set, HWC needs to update the panel on the next refresh, and when // we receive the fence back, we know that the process was complete. It returns whether // we need to wait for the next invalidate bool performSetActiveConfig() REQUIRES(mStateLock); + // Called when active config is no longer is progress + void desiredActiveConfigChangeDone() REQUIRES(mStateLock); // called on the main thread in response to setPowerMode() void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock); @@ -537,9 +553,9 @@ private: void updateInputFlinger(); void updateInputWindowInfo(); void commitInputWindowCommands() REQUIRES(mStateLock); - void executeInputWindowCommands(); void setInputWindowsFinished(); void updateCursorAsync(); + void initScheduler(DisplayId primaryDisplayId); /* handlePageFlip - latch a new buffer if available and compute the dirty * region. Returns whether a new buffer has been latched, i.e., whether it @@ -832,7 +848,8 @@ private: return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt; } - bool previousFrameMissed(); + bool previousFrameMissed(int graceTimeMs = 0); + void setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled); /* * Debugging & dumpsys @@ -1103,6 +1120,7 @@ private: ui::Dataspace mDefaultCompositionDataspace; ui::Dataspace mWideColorGamutCompositionDataspace; + ui::Dataspace mColorSpaceAgnosticDataspace; SurfaceFlingerBE mBE; std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine; @@ -1115,8 +1133,8 @@ private: sp<Scheduler::ConnectionHandle> mAppConnectionHandle; sp<Scheduler::ConnectionHandle> mSfConnectionHandle; - scheduler::RefreshRateConfigs mRefreshRateConfigs; - scheduler::RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, *mTimeStats}; + std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; + std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; // All configs are allowed if the set is empty. using DisplayConfigs = std::set<int32_t>; @@ -1167,6 +1185,12 @@ private: // The Layer pointer is removed from the set when the destructor is called so there shouldn't // be any issues with a raw pointer referencing an invalid object. std::unordered_set<Layer*> mOffscreenLayers; + + // Flags to capture the state of Vsync in HWC + HWC2::Vsync mHWCVsyncState = HWC2::Vsync::Disable; + HWC2::Vsync mHWCVsyncPendingState = HWC2::Vsync::Disable; + + nsecs_t mExpectedPresentTime; }; } // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index 2b33ba174..b4716eb61 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -218,6 +218,22 @@ int32_t wcg_composition_pixel_format(PixelFormat defaultValue) { return static_cast<int32_t>(defaultValue); } +int64_t color_space_agnostic_dataspace(Dataspace defaultValue) { + auto temp = SurfaceFlingerProperties::color_space_agnostic_dataspace(); + if (temp.has_value()) { + return *temp; + } + return static_cast<int64_t>(defaultValue); +} + +bool refresh_rate_switching(bool defaultValue) { + auto temp = SurfaceFlingerProperties::refresh_rate_switching(); + if (temp.has_value()) { + return *temp; + } + return defaultValue; +} + int32_t set_idle_timer_ms(int32_t defaultValue) { auto temp = SurfaceFlingerProperties::set_idle_timer_ms(); if (temp.has_value()) { @@ -234,6 +250,14 @@ int32_t set_touch_timer_ms(int32_t defaultValue) { return defaultValue; } +int32_t set_display_power_timer_ms(int32_t defaultValue) { + auto temp = SurfaceFlingerProperties::set_display_power_timer_ms(); + if (temp.has_value()) { + return *temp; + } + return defaultValue; +} + bool use_smart_90_for_video(bool defaultValue) { auto temp = SurfaceFlingerProperties::use_smart_90_for_video(); if (temp.has_value()) { diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index 1964ccd58..e394ccab7 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -70,10 +70,17 @@ int64_t wcg_composition_dataspace( int32_t wcg_composition_pixel_format( android::hardware::graphics::common::V1_2::PixelFormat defaultValue); +int64_t color_space_agnostic_dataspace( + android::hardware::graphics::common::V1_2::Dataspace defaultValue); + +bool refresh_rate_switching(bool defaultValue); + int32_t set_idle_timer_ms(int32_t defaultValue); int32_t set_touch_timer_ms(int32_t defaultValue); +int32_t set_display_power_timer_ms(int32_t defaultValue); + bool use_smart_90_for_video(bool defaultValue); bool enable_protected_contents(bool defaultValue); diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index decabd5e8..000f21c54 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -15,7 +15,7 @@ module: "android.sysprop.SurfaceFlingerProperties" owner: Platform -# The following two propertiess define (respectively): +# The following two properties define (respectively): # # - The phase offset between hardware vsync and when apps are woken up by the # Choreographer callback @@ -251,6 +251,20 @@ prop { prop_name: "ro.surface_flinger.wcg_composition_pixel_format" } +# colorSpaceAgnosticDataspace specifies the data space that +# SurfaceFlinger expects for surfaces which are color space agnostic. +# The variable works only when useColorManagement is specified. If +# unspecified, the data space follows what SurfaceFlinger expects for +# surfaces when useColorManagement is specified. + +prop { + api_name: "color_space_agnostic_dataspace" + type: Long + scope: System + access: Readonly + prop_name: "ro.surface_flinger.color_space_agnostic_dataspace" +} + # Return the native panel primary data. The data includes red, green, # blue and white. The primary format is CIE 1931 XYZ color space. # If unspecified, the primaries is sRGB gamut by default. @@ -287,6 +301,18 @@ prop { prop_name: "ro.surface_flinger.display_primary_white" } +# refreshRateSwitching indicates whether SurfaceFlinger should use refresh rate +# switching on the device, e.g. to switch between 60 and 90 Hz. The settings +# below that are related to refresh rate switching will only have an effect if +# refresh_rate_switching is enabled. +prop { + api_name: "refresh_rate_switching" + type: Boolean + scope: System + access: Readonly + prop_name: "ro.surface_flinger.refresh_rate_switching" +} + # setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is # used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower # refresh rate. Setting this property to 0 means there is no timer. @@ -309,6 +335,18 @@ prop { prop_name: "ro.surface_flinger.set_touch_timer_ms" } +# setDisplayPowerTimerMs indicates what is considered a timeout in milliseconds for Scheduler. +# This value is used by the Scheduler to trigger display power inactivity callbacks that will +# keep the display in peak refresh rate as long as display power is not in normal mode. +# Setting this property to 0 means there is no timer. +prop { + api_name: "set_display_power_timer_ms" + type: Integer + scope: System + access: Readonly + prop_name: "ro.surface_flinger.set_display_power_timer_ms" +} + # useSmart90ForVideo indicates whether Scheduler should detect content FPS, and try to adjust the # screen refresh rate based on that. prop { diff --git a/services/surfaceflinger/sysprop/api/system-current.txt b/services/surfaceflinger/sysprop/api/system-current.txt index 6ae3ac15e..2e804c8a3 100644 --- a/services/surfaceflinger/sysprop/api/system-current.txt +++ b/services/surfaceflinger/sysprop/api/system-current.txt @@ -2,6 +2,7 @@ package android.sysprop { public final class SurfaceFlingerProperties { + method public static java.util.Optional<java.lang.Long> color_space_agnostic_dataspace(); method public static java.util.Optional<java.lang.Long> default_composition_dataspace(); method public static java.util.Optional<java.lang.Integer> default_composition_pixel_format(); method public static java.util.List<java.lang.Double> display_primary_blue(); @@ -16,7 +17,9 @@ package android.sysprop { method public static java.util.Optional<java.lang.Long> max_virtual_display_dimension(); method public static java.util.Optional<java.lang.Long> present_time_offset_from_vsync_ns(); method public static java.util.Optional<android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values> primary_display_orientation(); + method public static java.util.Optional<java.lang.Boolean> refresh_rate_switching(); method public static java.util.Optional<java.lang.Boolean> running_without_sync_framework(); + method public static java.util.Optional<java.lang.Integer> set_display_power_timer_ms(); method public static java.util.Optional<java.lang.Integer> set_idle_timer_ms(); method public static java.util.Optional<java.lang.Integer> set_touch_timer_ms(); method public static java.util.Optional<java.lang.Boolean> start_graphics_allocator_service(); diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp index d5f65348d..c93e15ef9 100644 --- a/services/surfaceflinger/tests/Transaction_test.cpp +++ b/services/surfaceflinger/tests/Transaction_test.cpp @@ -28,6 +28,7 @@ #include <binder/ProcessState.h> #include <gui/BufferItemConsumer.h> +#include <gui/IProducerListener.h> #include <gui/ISurfaceComposer.h> #include <gui/LayerState.h> #include <gui/Surface.h> @@ -6059,4 +6060,97 @@ TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) { } } +// This test ensures that when we drop an app buffer in SurfaceFlinger, we merge +// the dropped buffer's damage region into the next buffer's damage region. If +// we don't do this, we'll report an incorrect damage region to hardware +// composer, resulting in broken rendering. This test checks the BufferQueue +// case. +// +// Unfortunately, we don't currently have a way to inspect the damage region +// SurfaceFlinger sends to hardware composer from a test, so this test requires +// the dev to manually watch the device's screen during the test to spot broken +// rendering. Because the results can't be automatically verified, this test is +// marked disabled. +TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDroppingBuffers) { + const int width = mDisplayWidth; + const int height = mDisplayHeight; + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height)); + const auto producer = layer->getIGraphicBufferProducer(); + const sp<IProducerListener> dummyListener(new DummyProducerListener); + IGraphicBufferProducer::QueueBufferOutput queueBufferOutput; + ASSERT_EQ(OK, + producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput)); + + std::map<int, sp<GraphicBuffer>> slotMap; + auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) { + ASSERT_NE(nullptr, buf); + const auto iter = slotMap.find(slot); + ASSERT_NE(slotMap.end(), iter); + *buf = iter->second; + }; + + auto dequeue = [&](int* outSlot) { + ASSERT_NE(nullptr, outSlot); + *outSlot = -1; + int slot; + sp<Fence> fence; + uint64_t age; + FrameEventHistoryDelta timestamps; + const status_t dequeueResult = + producer->dequeueBuffer(&slot, &fence, width, height, PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + &age, ×tamps); + if (dequeueResult == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { + sp<GraphicBuffer> newBuf; + ASSERT_EQ(OK, producer->requestBuffer(slot, &newBuf)); + ASSERT_NE(nullptr, newBuf.get()); + slotMap[slot] = newBuf; + } else { + ASSERT_EQ(OK, dequeueResult); + } + *outSlot = slot; + }; + + auto queue = [&](int slot, const Region& damage, nsecs_t displayTime) { + IGraphicBufferProducer::QueueBufferInput input( + /*timestamp=*/displayTime, /*isAutoTimestamp=*/false, HAL_DATASPACE_UNKNOWN, + /*crop=*/Rect::EMPTY_RECT, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, + /*transform=*/0, Fence::NO_FENCE); + input.setSurfaceDamage(damage); + IGraphicBufferProducer::QueueBufferOutput output; + ASSERT_EQ(OK, producer->queueBuffer(slot, input, &output)); + }; + + auto fillAndPostBuffers = [&](const Color& color) { + int slot1; + ASSERT_NO_FATAL_FAILURE(dequeue(&slot1)); + int slot2; + ASSERT_NO_FATAL_FAILURE(dequeue(&slot2)); + + sp<GraphicBuffer> buf1; + ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot1, &buf1)); + sp<GraphicBuffer> buf2; + ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot2, &buf2)); + fillGraphicBufferColor(buf1, Rect(width, height), color); + fillGraphicBufferColor(buf2, Rect(width, height), color); + + const auto displayTime = systemTime() + milliseconds_to_nanoseconds(100); + ASSERT_NO_FATAL_FAILURE(queue(slot1, Region::INVALID_REGION, displayTime)); + ASSERT_NO_FATAL_FAILURE( + queue(slot2, Region(Rect(width / 3, height / 3, 2 * width / 3, 2 * height / 3)), + displayTime)); + }; + + const auto startTime = systemTime(); + const std::array<Color, 3> colors = {Color::RED, Color::GREEN, Color::BLUE}; + int colorIndex = 0; + while (nanoseconds_to_seconds(systemTime() - startTime) < 10) { + ASSERT_NO_FATAL_FAILURE(fillAndPostBuffers(colors[colorIndex++ % colors.size()])); + std::this_thread::sleep_for(1s); + } + + ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU)); +} + } // namespace android diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 4f8ed1ae1..349dd3f2f 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -125,7 +125,17 @@ public: } void setupScheduler() { - mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs()); + std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}}; + mFlinger.mutableRefreshRateConfigs() = + std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, + configs, + /*currentConfig=*/0); + mFlinger.mutableRefreshRateStats() = + std::make_unique<scheduler::RefreshRateStats>(*mFlinger.mutableRefreshRateConfigs(), + *mFlinger.mutableTimeStats(), + /*currentConfig=*/0, + /*powerMode=*/HWC_POWER_MODE_OFF); + mScheduler = new TestableScheduler(*mFlinger.mutableRefreshRateConfigs()); mScheduler->mutableEventControlThread().reset(mEventControlThread); mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync); EXPECT_CALL(*mEventThread.get(), registerDisplayEventConnection(_)); diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp index 2e705dad6..0aa8cf565 100644 --- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp +++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp @@ -51,6 +51,7 @@ protected: AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder; static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms; + static constexpr std::chrono::nanoseconds mOffsetThresholdForNextVsync = 16ms; static constexpr int mIterations = 100; }; @@ -78,7 +79,8 @@ void DispSyncSourceTest::createDispSync() { void DispSyncSourceTest::createDispSyncSource() { createDispSync(); - mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true, + mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), + mOffsetThresholdForNextVsync.count(), true, "DispSyncSourceTest"); mDispSyncSource->setCallback(this); } diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 5f58e7dce..f40996eec 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -29,6 +29,7 @@ #include <ui/DebugUtils.h> #include "DisplayIdentificationTest.h" +#include "Scheduler/RefreshRateConfigs.h" #include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" @@ -179,7 +180,16 @@ DisplayTransactionTest::~DisplayTransactionTest() { } void DisplayTransactionTest::setupScheduler() { - mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs()); + std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}}; + mFlinger.mutableRefreshRateConfigs() = + std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs, + /*currentConfig=*/0); + mFlinger.mutableRefreshRateStats() = + std::make_unique<scheduler::RefreshRateStats>(*mFlinger.mutableRefreshRateConfigs(), + *mFlinger.mutableTimeStats(), + /*currentConfig=*/0, + /*powerMode=*/HWC_POWER_MODE_OFF); + mScheduler = new TestableScheduler(*mFlinger.mutableRefreshRateConfigs()); mScheduler->mutableEventControlThread().reset(mEventControlThread); mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync); EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)); diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index ea908a901..dbd9b8403 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -55,8 +55,9 @@ protected: class MockEventThreadConnection : public EventThreadConnection { public: MockEventThreadConnection(android::impl::EventThread* eventThread, - ResyncCallback&& resyncCallback) - : EventThreadConnection(eventThread, std::move(resyncCallback)) {} + ResyncCallback&& resyncCallback, + ISurfaceComposer::ConfigChanged configChanged) + : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {} MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event)); }; @@ -67,7 +68,8 @@ protected: ~EventThreadTest() override; void createThread(); - sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder); + sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder, + ISurfaceComposer::ConfigChanged configChanged); void expectVSyncSetEnabledCallReceived(bool expectedState); void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset); @@ -110,7 +112,8 @@ EventThreadTest::EventThreadTest() { .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable())); createThread(); - mConnection = createConnection(mConnectionEventCallRecorder); + mConnection = createConnection(mConnectionEventCallRecorder, + ISurfaceComposer::eConfigChangedDispatch); // A display must be connected for VSYNC events to be delivered. mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true); @@ -138,9 +141,10 @@ void EventThreadTest::createThread() { } sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection( - ConnectionEventRecorder& recorder) { + ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged) { sp<MockEventThreadConnection> connection = - new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable()); + new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(), + configChanged); EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable())); return connection; } @@ -267,7 +271,9 @@ TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) { TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) { // Create a first connection, register it, and request a vsync rate of zero. ConnectionEventRecorder firstConnectionEventRecorder{0}; - sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder); + sp<MockEventThreadConnection> firstConnection = + createConnection(firstConnectionEventRecorder, + ISurfaceComposer::eConfigChangedSuppress); mThread->setVsyncRate(0, firstConnection); // By itself, this should not enable vsync events @@ -277,7 +283,8 @@ TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) { // However if there is another connection which wants events at a nonzero rate..... ConnectionEventRecorder secondConnectionEventRecorder{0}; sp<MockEventThreadConnection> secondConnection = - createConnection(secondConnectionEventRecorder); + createConnection(secondConnectionEventRecorder, + ISurfaceComposer::eConfigChangedSuppress); mThread->setVsyncRate(1, secondConnection); // EventThread should enable vsync callbacks. @@ -363,7 +370,9 @@ TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) { TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) { ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY}; - sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder); + sp<MockEventThreadConnection> errorConnection = + createConnection(errorConnectionEventRecorder, + ISurfaceComposer::eConfigChangedSuppress); mThread->setVsyncRate(1, errorConnection); // EventThread should enable vsync callbacks. @@ -387,7 +396,9 @@ TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) { TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) { ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK}; - sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder); + sp<MockEventThreadConnection> errorConnection = + createConnection(errorConnectionEventRecorder, + ISurfaceComposer::eConfigChangedSuppress); mThread->setVsyncRate(1, errorConnection); // EventThread should enable vsync callbacks. @@ -449,5 +460,18 @@ TEST_F(EventThreadTest, postConfigChangedPrimary64bit) { expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7); } +TEST_F(EventThreadTest, suppressConfigChanged) { + ConnectionEventRecorder suppressConnectionEventRecorder{0}; + sp<MockEventThreadConnection> suppressConnection = + createConnection(suppressConnectionEventRecorder, + ISurfaceComposer::eConfigChangedSuppress); + + mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 9); + expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9); + + auto args = suppressConnectionEventRecorder.waitForCall(); + ASSERT_FALSE(args.has_value()); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h index 96121bb08..1d7501102 100644 --- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h +++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h @@ -23,6 +23,8 @@ namespace android { namespace scheduler { +using RefreshRateType = RefreshRateConfigs::RefreshRateType; + class FakePhaseOffsets : public android::scheduler::PhaseOffsets { nsecs_t FAKE_PHASE_OFFSET_NS = 0; @@ -34,20 +36,20 @@ public: nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; } PhaseOffsets::Offsets getOffsetsForRefreshRate( - RefreshRateConfigs::RefreshRateType /*refreshRateType*/) const override { + RefreshRateType /*refreshRateType*/) const override { return getCurrentOffsets(); } // Returns early, early GL, and late offsets for Apps and SF. PhaseOffsets::Offsets getCurrentOffsets() const override { - return Offsets{{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}, - {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}, - {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}}; + return Offsets{{RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}, + {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}, + {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}}; } // This function should be called when the device is switching between different // refresh rates, to properly update the offsets. - void setRefreshRateType(RefreshRateConfigs::RefreshRateType /*refreshRateType*/) override {} + void setRefreshRateType(RefreshRateType /*refreshRateType*/) override {} nsecs_t getOffsetThresholdForNextVsync() const override { return FAKE_PHASE_OFFSET_NS; } @@ -56,4 +58,4 @@ public: }; } // namespace scheduler -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 2b1dfa8f3..8e7440c2e 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -25,7 +25,17 @@ public: protected: std::unique_ptr<LayerHistory> mLayerHistory; + static constexpr float MIN_REFRESH_RATE = 30.f; static constexpr float MAX_REFRESH_RATE = 90.f; + static constexpr auto RELEVANT_FRAME_THRESHOLD = 90u; + static constexpr uint64_t THIRTY_FPS_INTERVAL = 33'333'333; + + void forceRelevancy(const std::unique_ptr<LayerHistory::LayerHandle>& testLayer) { + mLayerHistory->setVisibility(testLayer, true); + for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) { + mLayerHistory->insert(testLayer, 0, false /*isHDR*/); + } + }; }; LayerHistoryTest::LayerHistoryTest() { @@ -36,31 +46,25 @@ LayerHistoryTest::~LayerHistoryTest() {} namespace { TEST_F(LayerHistoryTest, oneLayer) { std::unique_ptr<LayerHistory::LayerHandle> testLayer = - mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE); + mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE); mLayerHistory->setVisibility(testLayer, true); + for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) { + EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first); + mLayerHistory->insert(testLayer, 0, false /*isHDR*/); + } - mLayerHistory->insert(testLayer, 0, false /*isHDR*/); - EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first); - - mLayerHistory->insert(testLayer, 0, false /*isHDR*/); - mLayerHistory->insert(testLayer, 0, false /*isHDR*/); - mLayerHistory->insert(testLayer, 0, false /*isHDR*/); - // This is still 0, because the layer is not considered recently active if it - // has been present in less than 10 frames. - EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first); - mLayerHistory->insert(testLayer, 0, false /*isHDR*/); - mLayerHistory->insert(testLayer, 0, false /*isHDR*/); - mLayerHistory->insert(testLayer, 0, false /*isHDR*/); - mLayerHistory->insert(testLayer, 0, false /*isHDR*/); - mLayerHistory->insert(testLayer, 0, false /*isHDR*/); - mLayerHistory->insert(testLayer, 0, false /*isHDR*/); - // This should be MAX_REFRESH_RATE as we have more than 10 samples - EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first); + // Add a few more. This time we should get MAX refresh rate as the layer + // becomes relevant + static constexpr auto A_FEW = 10; + for (auto i = 0u; i < A_FEW; i++) { + EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first); + mLayerHistory->insert(testLayer, 0, false /*isHDR*/); + } } TEST_F(LayerHistoryTest, oneHDRLayer) { std::unique_ptr<LayerHistory::LayerHandle> testLayer = - mLayerHistory->createLayer("TestHDRLayer", MAX_REFRESH_RATE); + mLayerHistory->createLayer("TestHDRLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE); mLayerHistory->setVisibility(testLayer, true); mLayerHistory->insert(testLayer, 0, true /*isHDR*/); @@ -74,12 +78,13 @@ TEST_F(LayerHistoryTest, oneHDRLayer) { TEST_F(LayerHistoryTest, explicitTimestamp) { std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer = - mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE); + mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE); mLayerHistory->setVisibility(test30FpsLayer, true); nsecs_t startTime = systemTime(); - for (int i = 0; i < 31; i++) { - mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/); + for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) { + mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL), + false /*isHDR*/); } EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first); @@ -87,29 +92,31 @@ TEST_F(LayerHistoryTest, explicitTimestamp) { TEST_F(LayerHistoryTest, multipleLayers) { std::unique_ptr<LayerHistory::LayerHandle> testLayer = - mLayerHistory->createLayer("TestLayer", MAX_REFRESH_RATE); + mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE); mLayerHistory->setVisibility(testLayer, true); std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer = - mLayerHistory->createLayer("30FpsLayer", MAX_REFRESH_RATE); + mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE); mLayerHistory->setVisibility(test30FpsLayer, true); std::unique_ptr<LayerHistory::LayerHandle> testLayer2 = - mLayerHistory->createLayer("TestLayer2", MAX_REFRESH_RATE); + mLayerHistory->createLayer("TestLayer2", MIN_REFRESH_RATE, MAX_REFRESH_RATE); mLayerHistory->setVisibility(testLayer2, true); nsecs_t startTime = systemTime(); - for (int i = 0; i < 10; i++) { + for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) { mLayerHistory->insert(testLayer, 0, false /*isHDR*/); } EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first); startTime = systemTime(); - for (int i = 0; i < 10; i++) { - mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/); + for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) { + mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL), + false /*isHDR*/); } EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first); - for (int i = 10; i < 30; i++) { - mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/); + for (int i = 10; i < RELEVANT_FRAME_THRESHOLD; i++) { + mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL), + false /*isHDR*/); } EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first); @@ -119,10 +126,12 @@ TEST_F(LayerHistoryTest, multipleLayers) { mLayerHistory->insert(testLayer2, 0, false /*isHDR*/); } EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first); - // After 100 ms frames become obsolete. - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - // Insert the 31st frame. - mLayerHistory->insert(test30FpsLayer, startTime + (30 * 33333333), false /*isHDR*/); + // After 1200 ms frames become obsolete. + std::this_thread::sleep_for(std::chrono::milliseconds(1500)); + + mLayerHistory->insert(test30FpsLayer, + startTime + (RELEVANT_FRAME_THRESHOLD * THIRTY_FPS_INTERVAL), + false /*isHDR*/); EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first); } diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 5067fe890..f315a8a86 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -23,7 +23,6 @@ #include "DisplayHardware/HWC2.h" #include "Scheduler/RefreshRateConfigs.h" -#include "mock/DisplayHardware/MockDisplay.h" using namespace std::chrono_literals; using testing::_; @@ -50,9 +49,8 @@ protected: ASSERT_EQ(left.configId, right.configId); ASSERT_EQ(left.name, right.name); ASSERT_EQ(left.fps, right.fps); + ASSERT_EQ(left.vsyncPeriod, right.vsyncPeriod); } - - RefreshRateConfigs mConfigs; }; RefreshRateConfigsTest::RefreshRateConfigsTest() { @@ -71,101 +69,39 @@ namespace { /* ------------------------------------------------------------------------ * Test cases */ -TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) { - std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs; - mConfigs.populate(displayConfigs); - - // We always store a configuration for screen off. - const auto& rates = mConfigs.getRefreshRates(); - ASSERT_EQ(1, rates.size()); - const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING); - ASSERT_NE(rates.end(), powerSavingRate); - ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE)); - ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT)); - - RefreshRate expectedConfig = - RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID}; - assertRatesEqual(expectedConfig, *powerSavingRate->second); - - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - assertRatesEqual(expectedConfig, *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); - ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - - // Sanity check that getRefreshRate() does not modify the underlying configs. - ASSERT_EQ(1, mConfigs.getRefreshRates().size()); +TEST_F(RefreshRateConfigsTest, oneDeviceConfig_isRejected) { + std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs, + /*currentConfig=*/0); + ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported()); } -TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) { - auto display = new Hwc2::mock::Display(); - std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs; - auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60); - config60.setVsyncPeriod(VSYNC_60); - displayConfigs.push_back(config60.build()); - mConfigs.populate(displayConfigs); +TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) { + std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}, + {HWC2_CONFIG_ID_90, VSYNC_90}}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs, + /*currentConfig=*/0); - const auto& rates = mConfigs.getRefreshRates(); + ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported()); + const auto& rates = refreshRateConfigs->getRefreshRateMap(); ASSERT_EQ(2, rates.size()); - const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING); - const auto& defaultRate = rates.find(RefreshRateType::DEFAULT); - ASSERT_NE(rates.end(), powerSavingRate); - ASSERT_NE(rates.end(), defaultRate); - ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE)); - - RefreshRate expectedPowerSavingConfig = - RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID}; - assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second); - RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60}; - assertRatesEqual(expectedDefaultConfig, *defaultRate->second); - - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - assertRatesEqual(expectedPowerSavingConfig, - *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); - - // Sanity check that getRefreshRate() does not modify the underlying configs. - ASSERT_EQ(2, mConfigs.getRefreshRates().size()); -} - -TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) { - auto display = new Hwc2::mock::Display(); - std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs; - auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60); - config60.setVsyncPeriod(VSYNC_60); - displayConfigs.push_back(config60.build()); - auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90); - config90.setVsyncPeriod(VSYNC_90); - displayConfigs.push_back(config90.build()); - mConfigs.populate(displayConfigs); - - const auto& rates = mConfigs.getRefreshRates(); - ASSERT_EQ(3, rates.size()); - const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING); const auto& defaultRate = rates.find(RefreshRateType::DEFAULT); const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE); - ASSERT_NE(rates.end(), powerSavingRate); ASSERT_NE(rates.end(), defaultRate); ASSERT_NE(rates.end(), performanceRate); - RefreshRate expectedPowerSavingConfig = - RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID}; - assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second); - RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60}; - assertRatesEqual(expectedDefaultConfig, *defaultRate->second); - RefreshRate expectedPerformanceConfig = - RefreshRate{CONFIG_ID_90, "90fps", 90, HWC2_CONFIG_ID_90}; - assertRatesEqual(expectedPerformanceConfig, *performanceRate->second); - - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - assertRatesEqual(expectedPowerSavingConfig, - *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); + RefreshRate expectedDefaultConfig = {CONFIG_ID_60, "60fps", 60, VSYNC_60, HWC2_CONFIG_ID_60}; + assertRatesEqual(expectedDefaultConfig, defaultRate->second); + RefreshRate expectedPerformanceConfig = {CONFIG_ID_90, "90fps", 90, VSYNC_90, + HWC2_CONFIG_ID_90}; + assertRatesEqual(expectedPerformanceConfig, performanceRate->second); + + assertRatesEqual(expectedDefaultConfig, + refreshRateConfigs->getRefreshRateFromType(RefreshRateType::DEFAULT)); assertRatesEqual(expectedPerformanceConfig, - *mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); + refreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE)); } } // namespace } // namespace scheduler diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp index 411ec6177..cec0b32a6 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp @@ -22,7 +22,6 @@ #include <thread> #include "Scheduler/RefreshRateStats.h" -#include "mock/DisplayHardware/MockDisplay.h" #include "mock/MockTimeStats.h" using namespace std::chrono_literals; @@ -42,9 +41,18 @@ protected: RefreshRateStatsTest(); ~RefreshRateStatsTest(); + void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) { + mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>( + /*refreshRateSwitching=*/true, configs, /*currentConfig=*/0); + mRefreshRateStats = + std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats, + /*currentConfig=*/0, + /*currentPowerMode=*/HWC_POWER_MODE_OFF); + } + mock::TimeStats mTimeStats; - RefreshRateConfigs mRefreshRateConfigs; - RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, mTimeStats}; + std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs; + std::unique_ptr<RefreshRateStats> mRefreshRateStats; }; RefreshRateStatsTest::RefreshRateStatsTest() { @@ -63,63 +71,46 @@ namespace { /* ------------------------------------------------------------------------ * Test cases */ -TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) { - std::vector<std::shared_ptr<const HWC2::Display::Config>> configs; - mRefreshRateConfigs.populate(configs); - - // There is one default config, so the refresh rates should have one item. - EXPECT_EQ(1, mRefreshRateStats.getTotalTimes().size()); -} - TEST_F(RefreshRateStatsTest, oneConfigTest) { - auto display = new Hwc2::mock::Display(); - - auto config = HWC2::Display::Config::Builder(*display, CONFIG_ID_90); - config.setVsyncPeriod(VSYNC_90); - std::vector<std::shared_ptr<const HWC2::Display::Config>> configs; - configs.push_back(config.build()); - - mRefreshRateConfigs.populate(configs); + init({{CONFIG_ID_90, VSYNC_90}}); EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1)); EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1)); - std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes(); - EXPECT_EQ(2, times.size()); + std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes(); + ASSERT_EQ(1, times.size()); EXPECT_NE(0u, times.count("ScreenOff")); - EXPECT_EQ(1u, times.count("90fps")); - EXPECT_EQ(0, times["90fps"]); // Setting up tests on mobile harness can be flaky with time passing, so testing for // exact time changes can result in flaxy numbers. To avoid that remember old // numbers to make sure the correct values are increasing in the next test. int screenOff = times["ScreenOff"]; - int ninety = times["90fps"]; // Screen is off by default. std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); - EXPECT_EQ(0, times["90fps"]); + EXPECT_EQ(0u, times.count("90fps")); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); - EXPECT_LT(ninety, times["90fps"]); + ASSERT_EQ(1u, times.count("90fps")); + EXPECT_LT(0, times["90fps"]); - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE); - ninety = mRefreshRateStats.getTotalTimes()["90fps"]; + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE); + int ninety = mRefreshRateStats->getTotalTimes()["90fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config // does not update refresh rates that come from the config. EXPECT_LT(screenOff, times["ScreenOff"]); @@ -127,93 +118,75 @@ TEST_F(RefreshRateStatsTest, oneConfigTest) { } TEST_F(RefreshRateStatsTest, twoConfigsTest) { - auto display = new Hwc2::mock::Display(); - - auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90); - config90.setVsyncPeriod(VSYNC_90); - std::vector<std::shared_ptr<const HWC2::Display::Config>> configs; - configs.push_back(config90.build()); - - auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60); - config60.setVsyncPeriod(VSYNC_60); - configs.push_back(config60.build()); - - mRefreshRateConfigs.populate(configs); + init({{CONFIG_ID_90, VSYNC_90}, {CONFIG_ID_60, VSYNC_60}}); EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1)); EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1)); EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1)); - std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes(); - EXPECT_EQ(3, times.size()); + std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes(); + ASSERT_EQ(1, times.size()); EXPECT_NE(0u, times.count("ScreenOff")); - EXPECT_EQ(1u, times.count("60fps")); - EXPECT_EQ(0, times["60fps"]); - EXPECT_EQ(1u, times.count("90fps")); - EXPECT_EQ(0, times["90fps"]); // Setting up tests on mobile harness can be flaky with time passing, so testing for // exact time changes can result in flaxy numbers. To avoid that remember old // numbers to make sure the correct values are increasing in the next test. int screenOff = times["ScreenOff"]; - int sixty = times["60fps"]; - int ninety = times["90fps"]; // Screen is off by default. std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); - EXPECT_EQ(sixty, times["60fps"]); - EXPECT_EQ(ninety, times["90fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); - EXPECT_EQ(sixty, times["60fps"]); - EXPECT_LT(ninety, times["90fps"]); + ASSERT_EQ(1u, times.count("90fps")); + EXPECT_LT(0, times["90fps"]); // When power mode is normal, time for configs updates. - mRefreshRateStats.setConfigMode(CONFIG_ID_60); - ninety = mRefreshRateStats.getTotalTimes()["90fps"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_60); + int ninety = mRefreshRateStats->getTotalTimes()["90fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); - EXPECT_LT(sixty, times["60fps"]); + ASSERT_EQ(1u, times.count("60fps")); + EXPECT_LT(0, times["60fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - sixty = mRefreshRateStats.getTotalTimes()["60fps"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + int sixty = mRefreshRateStats->getTotalTimes()["60fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); EXPECT_LT(ninety, times["90fps"]); EXPECT_EQ(sixty, times["60fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_60); - ninety = mRefreshRateStats.getTotalTimes()["90fps"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_60); + ninety = mRefreshRateStats->getTotalTimes()["90fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); EXPECT_LT(sixty, times["60fps"]); // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config // does not update refresh rates that come from the config. - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - sixty = mRefreshRateStats.getTotalTimes()["60fps"]; + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE); + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + sixty = mRefreshRateStats->getTotalTimes()["60fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); EXPECT_EQ(sixty, times["60fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_60); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_60); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); EXPECT_EQ(sixty, times["60fps"]); diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 1f8b11180..571fdfd9f 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -3,13 +3,13 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> - #include <log/log.h> #include <mutex> #include "Scheduler/EventControlThread.h" #include "Scheduler/EventThread.h" +#include "Scheduler/RefreshRateConfigs.h" #include "Scheduler/Scheduler.h" #include "mock/MockEventThread.h" @@ -25,7 +25,8 @@ protected: class MockEventThreadConnection : public android::EventThreadConnection { public: explicit MockEventThreadConnection(EventThread* eventThread) - : EventThreadConnection(eventThread, ResyncCallback()) {} + : EventThreadConnection(eventThread, ResyncCallback(), + ISurfaceComposer::eConfigChangedSuppress) {} ~MockEventThreadConnection() = default; MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel)); @@ -33,7 +34,7 @@ protected: MOCK_METHOD0(requestNextVsync, void()); }; - scheduler::RefreshRateConfigs mRefreshRateConfigs; + std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; /** * This mock Scheduler class uses implementation of mock::EventThread but keeps everything else @@ -47,7 +48,7 @@ protected: std::unique_ptr<EventThread> makeEventThread( const char* /* connectionName */, DispSync* /* dispSync */, - nsecs_t /* phaseOffsetNs */, + nsecs_t /* phaseOffsetNs */, nsecs_t /* offsetThresholdForNextVsync */, impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override { return std::move(mEventThread); } @@ -72,19 +73,24 @@ SchedulerTest::SchedulerTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}}; + mRefreshRateConfigs = + std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs, + /*currentConfig=*/0); + std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>(); mEventThread = eventThread.get(); - mScheduler = std::make_unique<MockScheduler>(mRefreshRateConfigs, std::move(eventThread)); + mScheduler = std::make_unique<MockScheduler>(*mRefreshRateConfigs, std::move(eventThread)); EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0)); mEventThreadConnection = new MockEventThreadConnection(mEventThread); // createConnection call to scheduler makes a createEventConnection call to EventThread. Make // sure that call gets executed and returns an EventThread::Connection object. - EXPECT_CALL(*mEventThread, createEventConnection(_)) + EXPECT_CALL(*mEventThread, createEventConnection(_, _)) .WillRepeatedly(Return(mEventThreadConnection)); - mConnectionHandle = mScheduler->createConnection("appConnection", 16, ResyncCallback(), + mConnectionHandle = mScheduler->createConnection("appConnection", 16, 16, impl::EventThread::InterceptVSyncsCallback()); EXPECT_TRUE(mConnectionHandle != nullptr); } @@ -105,7 +111,10 @@ TEST_F(SchedulerTest, testNullPtr) { // exceptions, just gracefully continues. sp<IDisplayEventConnection> returnedValue; ASSERT_NO_FATAL_FAILURE( - returnedValue = mScheduler->createDisplayEventConnection(nullptr, ResyncCallback())); + returnedValue = + mScheduler->createDisplayEventConnection(nullptr, + ISurfaceComposer:: + eConfigChangedSuppress)); EXPECT_TRUE(returnedValue == nullptr); EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr); EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr); @@ -126,7 +135,9 @@ TEST_F(SchedulerTest, invalidConnectionHandle) { sp<IDisplayEventConnection> returnedValue; ASSERT_NO_FATAL_FAILURE( returnedValue = - mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback())); + mScheduler->createDisplayEventConnection(connectionHandle, + ISurfaceComposer:: + eConfigChangedSuppress)); EXPECT_TRUE(returnedValue == nullptr); EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr); EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr); @@ -155,7 +166,9 @@ TEST_F(SchedulerTest, validConnectionHandle) { sp<IDisplayEventConnection> returnedValue; ASSERT_NO_FATAL_FAILURE( returnedValue = - mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback())); + mScheduler->createDisplayEventConnection(mConnectionHandle, + ISurfaceComposer:: + eConfigChangedSuppress)); EXPECT_TRUE(returnedValue != nullptr); ASSERT_EQ(returnedValue, mEventThreadConnection); diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index c3d2b8de4..cb6980ed1 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -17,6 +17,7 @@ #pragma once #include <gmock/gmock.h> +#include <gui/ISurfaceComposer.h> #include "Scheduler/EventThread.h" #include "Scheduler/RefreshRateConfigs.h" @@ -34,7 +35,8 @@ public: // Scheduler::Connection. This allows plugging in mock::EventThread. sp<Scheduler::ConnectionHandle> addConnection(std::unique_ptr<EventThread> eventThread) { sp<EventThreadConnection> eventThreadConnection = - new EventThreadConnection(eventThread.get(), ResyncCallback()); + new EventThreadConnection(eventThread.get(), ResyncCallback(), + ISurfaceComposer::eConfigChangedSuppress); const int64_t id = sNextId++; mConnections.emplace(id, std::make_unique<Scheduler::Connection>(new ConnectionHandle(id), diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 64d34ee10..1c1b0201d 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -32,11 +32,11 @@ #include "Layer.h" #include "NativeWindowSurface.h" #include "Scheduler/MessageQueue.h" +#include "Scheduler/RefreshRateConfigs.h" #include "StartPropertySetThread.h" #include "SurfaceFlinger.h" #include "SurfaceFlingerFactory.h" #include "SurfaceInterceptor.h" - #include "TimeStats/TimeStats.h" namespace android { @@ -342,6 +342,8 @@ public: auto& mutableAppConnectionHandle() { return mFlinger->mAppConnectionHandle; } auto& mutableSfConnectionHandle() { return mFlinger->mSfConnectionHandle; } auto& mutableRefreshRateConfigs() { return mFlinger->mRefreshRateConfigs; } + auto& mutableRefreshRateStats() { return mFlinger->mRefreshRateStats; } + auto& mutableTimeStats() { return mFlinger->mTimeStats; } ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h index 12a349dd7..9ca116d73 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h +++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h @@ -35,6 +35,7 @@ public: MOCK_METHOD0(endResync, void()); MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(getPeriod, nsecs_t()); + MOCK_METHOD0(getIntendedPeriod, nsecs_t()); MOCK_METHOD1(setRefreshSkipCount, void(int)); MOCK_CONST_METHOD1(computeNextRefresh, nsecs_t(int)); MOCK_METHOD1(setIgnorePresentFences, void(bool)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 5b5f8e7fa..ed35ebf2b 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -28,7 +28,8 @@ public: EventThread(); ~EventThread() override; - MOCK_CONST_METHOD1(createEventConnection, sp<EventThreadConnection>(ResyncCallback)); + MOCK_CONST_METHOD2(createEventConnection, + sp<EventThreadConnection>(ResyncCallback, ISurfaceComposer::ConfigChanged)); MOCK_METHOD0(onScreenReleased, void()); MOCK_METHOD0(onScreenAcquired, void()); MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool)); diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp index 71048db92..c3b5523b9 100644 --- a/vulkan/libvulkan/api.cpp +++ b/vulkan/libvulkan/api.cpp @@ -124,7 +124,7 @@ class OverrideLayerNames { }; void AddImplicitLayers() { - if (!is_instance_ || !driver::Debuggable()) + if (!is_instance_) return; GetLayersFromSettings(); @@ -370,7 +370,8 @@ class OverrideExtensionNames { private: bool EnableDebugCallback() const { - return (is_instance_ && driver::Debuggable() && + return (is_instance_ && + android::GraphicsEnv::getInstance().isDebuggable() && property_get_bool("debug.vulkan.enable_callback", false)); } diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index a53bb5933..8ea09805a 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -724,10 +724,6 @@ void FreeDeviceData(DeviceData* data, const VkAllocationCallbacks& allocator) { } // anonymous namespace -bool Debuggable() { - return (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) >= 0); -} - bool OpenHAL() { return Hal::Open(); } diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h index 57c956d9a..075a15b31 100644 --- a/vulkan/libvulkan/driver.h +++ b/vulkan/libvulkan/driver.h @@ -105,7 +105,6 @@ struct DeviceData { uint32_t driver_version; }; -bool Debuggable(); bool OpenHAL(); const VkAllocationCallbacks& GetDefaultAllocator(); diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp index af1adcff6..691f3b0f5 100644 --- a/vulkan/libvulkan/layers_extensions.cpp +++ b/vulkan/libvulkan/layers_extensions.cpp @@ -484,8 +484,7 @@ void* GetLayerGetProcAddr(const Layer& layer, void DiscoverLayers() { ATRACE_CALL(); - if (property_get_bool("ro.debuggable", false) && - prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { + if (android::GraphicsEnv::getInstance().isDebuggable()) { DiscoverLayersInPathList(kSystemLayerLibraryDir); } if (!android::GraphicsEnv::getInstance().getLayerPaths().empty()) |