diff options
134 files changed, 3898 insertions, 1690 deletions
@@ -1,3 +1,3 @@ -third_party { + third_party { license_type: NOTICE } diff --git a/cmds/servicemanager/Access.cpp b/cmds/servicemanager/Access.cpp index 809872417d..6e2abf65e2 100644 --- a/cmds/servicemanager/Access.cpp +++ b/cmds/servicemanager/Access.cpp @@ -34,7 +34,9 @@ constexpr bool kIsVendor = false; #ifdef __ANDROID__ static std::string getPidcon(pid_t pid) { - android_errorWriteLog(0x534e4554, "121035042"); + CHECK_EQ(nullptr, IPCThreadState::self()->getServingStackPointer()) + << "Did not get context from PID " << pid + << ". We should always get contexts from other processes."; char* lookup = nullptr; if (getpidcon(pid, &lookup) < 0) { diff --git a/include/android/looper.h b/include/android/looper.h index 8cf13965bb..409db848f8 100644 --- a/include/android/looper.h +++ b/include/android/looper.h @@ -106,7 +106,7 @@ enum { /** * Result from ALooper_pollOnce() and ALooper_pollAll(): * An error occurred. The poll may also have been explicitly woken by - * ALooper_wake(()). + * ALooper_wake(). */ ALOOPER_POLL_ERROR = -4, }; @@ -224,11 +224,11 @@ int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa * hidden and callers should migrate to ALooper_pollOnce. Binary compatibility * is preserved to support already-compiled apps. * - * \bug ALooper_pollAll will not wake in response to ALooper_wake calls if it + * \bug ALooper_pollAll() will not wake in response to ALooper_wake() calls if it * also handles another event at the same time. * - * \deprecated Calls to ALooper_pollAll should be replaced with - * ALooper_pollOnce. If you call ALooper_pollOnce in a loop, you *must* treat + * \deprecated Calls to ALooper_pollAll() should be replaced with + * ALooper_pollOnce(). If you call ALooper_pollOnce() in a loop, you *must* treat * all return values as if they also indicate ALOOPER_POLL_WAKE. */ int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) @@ -242,7 +242,7 @@ int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outDat * This method can be called on any thread. * This method returns immediately. * - * \bug ALooper_pollAll will not reliably wake in response to ALooper_wake. + * \bug ALooper_pollAll() will not reliably wake in response to ALooper_wake(). */ void ALooper_wake(ALooper* looper); diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 2b4a5f5f53..9ea6549e8a 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -478,8 +478,13 @@ void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* _Nonnull aWorkDurati /** * Return the APerformanceHintSession wrapped by a Java PerformanceHintManager.Session object. * - * The Java session maintains ownership over the wrapped native session, so it cannot be - * closed using {@link APerformanceHint_closeSession}. + * The Java session maintains ownership over the wrapped native session, so it cannot be closed + * using {@link APerformanceHint_closeSession}. The return value is valid until the Java object + * containing this value dies. + * + * The returned pointer is intended to be used by JNI calls to access native performance APIs using + * a Java hint session wrapper, and then immediately discarded. Using the pointer after the death of + * the Java container results in undefined behavior. * * @param env The Java environment where the PerformanceHintManager.Session lives. * @param sessionObj The Java Session to unwrap. diff --git a/include/android/system_health.h b/include/android/system_health.h index 69620df16e..352eb72b0c 100644 --- a/include/android/system_health.h +++ b/include/android/system_health.h @@ -81,10 +81,10 @@ typedef struct AGpuHeadroomParams AGpuHeadroomParams; * * @return A new instance of ACpuHeadroomParams. */ -ACpuHeadroomParams *_Nonnull ACpuHeadroomParams_create() +ACpuHeadroomParams* _Nonnull ACpuHeadroomParams_create(void) __INTRODUCED_IN(36); -enum ACpuHeadroomCalculationType { +typedef enum ACpuHeadroomCalculationType : int32_t { /** * Use the minimum headroom value within the calculation window. * Introduced in API level 36. @@ -95,10 +95,9 @@ enum ACpuHeadroomCalculationType { * Introduced in API level 36. */ ACPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1, -}; -typedef enum ACpuHeadroomCalculationType ACpuHeadroomCalculationType; +} ACpuHeadroomCalculationType; -enum AGpuHeadroomCalculationType { +typedef enum AGpuHeadroomCalculationType : int32_t { /** * Use the minimum headroom value within the calculation window. * Introduced in API level 36. @@ -109,8 +108,7 @@ enum AGpuHeadroomCalculationType { * Introduced in API level 36. */ AGPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1, -}; -typedef enum AGpuHeadroomCalculationType AGpuHeadroomCalculationType; +} AGpuHeadroomCalculationType; /** * Sets the headroom calculation window size in ACpuHeadroomParams. @@ -124,7 +122,7 @@ typedef enum AGpuHeadroomCalculationType AGpuHeadroomCalculationType; * {@link #ACpuHeadroomParams_getCalculationWindowMillis} if not set. The device * will try to use the closest feasible window size to this param. */ -void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams *_Nonnull params, +void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params, int windowMillis) __INTRODUCED_IN(36); @@ -136,7 +134,7 @@ __INTRODUCED_IN(36); * @param params The params to be set. * @return This will return the default value chosen by the device if the params is not set. */ -int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams *_Nonnull params) +int ACpuHeadroomParams_getCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params) __INTRODUCED_IN(36); /** @@ -151,7 +149,7 @@ __INTRODUCED_IN(36); * {@link #AGpuHeadroomParams_getCalculationWindowMillis} if not set. The device * will try to use the closest feasible window size to this param. */ -void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams *_Nonnull params, +void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params, int windowMillis) __INTRODUCED_IN(36); @@ -163,7 +161,7 @@ __INTRODUCED_IN(36); * @param params The params to be set. * @return This will return the default value chosen by the device if the params is not set. */ -int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams *_Nonnull params) +int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params) __INTRODUCED_IN(36); /** @@ -174,7 +172,7 @@ __INTRODUCED_IN(36); * @param params The params to be set. * @param calculationType The headroom calculation type. */ -void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams *_Nonnull params, +void ACpuHeadroomParams_setCalculationType(ACpuHeadroomParams* _Nonnull params, ACpuHeadroomCalculationType calculationType) __INTRODUCED_IN(36); @@ -187,7 +185,7 @@ __INTRODUCED_IN(36); * @return The headroom calculation type. */ ACpuHeadroomCalculationType -ACpuHeadroomParams_getCalculationType(ACpuHeadroomParams *_Nonnull params) +ACpuHeadroomParams_getCalculationType(ACpuHeadroomParams* _Nonnull params) __INTRODUCED_IN(36); /** @@ -198,7 +196,7 @@ __INTRODUCED_IN(36); * @param params The params to be set. * @param calculationType The headroom calculation type. */ -void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams *_Nonnull params, +void AGpuHeadroomParams_setCalculationType(AGpuHeadroomParams* _Nonnull params, AGpuHeadroomCalculationType calculationType) __INTRODUCED_IN(36); @@ -211,7 +209,7 @@ __INTRODUCED_IN(36); * @return The headroom calculation type. */ AGpuHeadroomCalculationType -AGpuHeadroomParams_getCalculationType(AGpuHeadroomParams *_Nonnull params) +AGpuHeadroomParams_getCalculationType(AGpuHeadroomParams* _Nonnull params) __INTRODUCED_IN(36); /** @@ -223,7 +221,7 @@ __INTRODUCED_IN(36); * @param tids Non-null array of TIDs, maximum 5. * @param tidsSize The size of the tids array. */ -void ACpuHeadroomParams_setTids(ACpuHeadroomParams *_Nonnull params, const int *_Nonnull tids, +void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int* _Nonnull tids, int tidsSize) __INTRODUCED_IN(36); @@ -238,7 +236,7 @@ __INTRODUCED_IN(36); * * @return A new instance of AGpuHeadroomParams. */ -AGpuHeadroomParams *_Nonnull AGpuHeadroomParams_create() +AGpuHeadroomParams* _Nonnull AGpuHeadroomParams_create(void) __INTRODUCED_IN(36); /** @@ -248,7 +246,7 @@ __INTRODUCED_IN(36); * * @param params The params to be deleted. */ -void ACpuHeadroomParams_destroy(ACpuHeadroomParams *_Nonnull params) +void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nonnull params) __INTRODUCED_IN(36); /** @@ -258,7 +256,7 @@ __INTRODUCED_IN(36); * * @param params The params to be deleted. */ -void AGpuHeadroomParams_destroy(AGpuHeadroomParams *_Nonnull params) +void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nonnull params) __INTRODUCED_IN(36); /** @@ -282,8 +280,8 @@ __INTRODUCED_IN(36); * EPERM if the TIDs do not belong to the same process. * ENOTSUP if API or requested params is unsupported. */ -int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams *_Nullable params, - float *_Nonnull outHeadroom) +int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams* _Nullable params, + float* _Nonnull outHeadroom) __INTRODUCED_IN(36); /** @@ -306,8 +304,8 @@ __INTRODUCED_IN(36); * EPIPE if failed to get the GPU headroom. * ENOTSUP if API or requested params is unsupported. */ -int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams *_Nullable params, - float *_Nonnull outHeadroom) +int ASystemHealth_getGpuHeadroom(const AGpuHeadroomParams* _Nullable params, + float* _Nonnull outHeadroom) __INTRODUCED_IN(36); /** @@ -323,7 +321,7 @@ __INTRODUCED_IN(36); * EPIPE if failed to get the minimum polling interval. * ENOTSUP if API is unsupported. */ -int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t *_Nonnull outMinIntervalMillis) +int ASystemHealth_getCpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis) __INTRODUCED_IN(36); /** @@ -339,7 +337,7 @@ __INTRODUCED_IN(36); * EPIPE if failed to get the minimum polling interval. * ENOTSUP if API is unsupported. */ -int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t *_Nonnull outMinIntervalMillis) +int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinIntervalMillis) __INTRODUCED_IN(36); #ifdef __cplusplus diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 6698d0c0cd..623e7b9139 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -775,6 +775,7 @@ void IPCThreadState::joinThreadPool(bool isMain) { LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); + mProcess->checkExpectingThreadPoolStart(); mProcess->mCurrentThreads++; mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index a5f416f1ba..0b7cd8154d 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -156,7 +156,7 @@ enum { #ifdef BINDER_WITH_KERNEL_IPC static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj, - const void* who) { + const void* who, bool tagFds) { switch (obj.hdr.type) { case BINDER_TYPE_BINDER: if (obj.binder) { @@ -173,7 +173,7 @@ static void acquire_object(const sp<ProcessState>& proc, const flat_binder_objec return; } case BINDER_TYPE_FD: { - if (obj.cookie != 0) { // owned + if (tagFds && obj.cookie != 0) { // owned FdTag(obj.handle, nullptr, who); } return; @@ -611,7 +611,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { } } - acquire_object(proc, *flat, this); + acquire_object(proc, *flat, this, true /*tagFds*/); } } #else @@ -1797,13 +1797,22 @@ restart_write: // Need to write meta-data? if (nullMetaData || val.binder != 0) { kernelFields->mObjects[kernelFields->mObjectsSize] = mDataPos; - acquire_object(ProcessState::self(), val, this); + acquire_object(ProcessState::self(), val, this, true /*tagFds*/); kernelFields->mObjectsSize++; } return finishWrite(sizeof(flat_binder_object)); } + if (mOwner) { + // continueWrite does have the logic to convert this from an + // owned to an unowned Parcel. However, this is pretty inefficient, + // and it's really strange to need to do so, so prefer to avoid + // these paths than try to support them. + ALOGE("writing objects not supported on owned Parcels"); + return PERMISSION_DENIED; + } + if (!enoughData) { const status_t err = growData(sizeof(val)); if (err != NO_ERROR) return err; @@ -2719,6 +2728,65 @@ size_t Parcel::ipcObjectsCount() const return 0; } +static void do_nothing_release_func(const uint8_t* data, size_t dataSize, + const binder_size_t* objects, size_t objectsCount) { + (void)data; + (void)dataSize; + (void)objects; + (void)objectsCount; +} +static void delete_data_release_func(const uint8_t* data, size_t dataSize, + const binder_size_t* objects, size_t objectsCount) { + delete[] data; + (void)dataSize; + (void)objects; + (void)objectsCount; +} + +void Parcel::makeDangerousViewOf(Parcel* p) { + if (p->isForRpc()) { + // warning: this must match the logic in rpcSetDataReference + auto* rf = p->maybeRpcFields(); + LOG_ALWAYS_FATAL_IF(rf == nullptr); + std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>> fds; + if (rf->mFds) { + fds.reserve(rf->mFds->size()); + for (const auto& fd : *rf->mFds) { + fds.push_back(binder::borrowed_fd(toRawFd(fd))); + } + } + status_t result = + rpcSetDataReference(rf->mSession, p->mData, p->mDataSize, + rf->mObjectPositions.data(), rf->mObjectPositions.size(), + std::move(fds), do_nothing_release_func); + LOG_ALWAYS_FATAL_IF(result != OK, "Failed: %s", statusToString(result).c_str()); + } else { +#ifdef BINDER_WITH_KERNEL_IPC + // warning: this must match the logic in ipcSetDataReference + auto* kf = p->maybeKernelFields(); + LOG_ALWAYS_FATAL_IF(kf == nullptr); + + // Ownership of FDs is passed to the Parcel from kernel binder. This should be refactored + // to move this ownership out of Parcel and into release_func. However, today, Parcel + // always assums it can own and close FDs today. So, for purposes of testing consistency, + // , create new FDs it can own. + + uint8_t* newData = new uint8_t[p->mDataSize]; // deleted by delete_data_release_func + memcpy(newData, p->mData, p->mDataSize); + for (size_t i = 0; i < kf->mObjectsSize; i++) { + flat_binder_object* flat = + reinterpret_cast<flat_binder_object*>(newData + kf->mObjects[i]); + if (flat->hdr.type == BINDER_TYPE_FD) { + flat->handle = fcntl(flat->handle, F_DUPFD_CLOEXEC, 0); + } + } + + ipcSetDataReference(newData, p->mDataSize, kf->mObjects, kf->mObjectsSize, + delete_data_release_func); +#endif // BINDER_WITH_KERNEL_IPC + } +} + void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount, release_func relFunc) { // this code uses 'mOwner == nullptr' to understand whether it owns memory @@ -2729,6 +2797,7 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, const bin auto* kernelFields = maybeKernelFields(); LOG_ALWAYS_FATAL_IF(kernelFields == nullptr); // guaranteed by freeData. + // must match makeDangerousViewOf mData = const_cast<uint8_t*>(data); mDataSize = mDataCapacity = dataSize; kernelFields->mObjects = const_cast<binder_size_t*>(objects); @@ -2807,6 +2876,7 @@ status_t Parcel::rpcSetDataReference( auto* rpcFields = maybeRpcFields(); LOG_ALWAYS_FATAL_IF(rpcFields == nullptr); // guaranteed by markForRpc. + // must match makeDangerousViewOf mData = const_cast<uint8_t*>(data); mDataSize = mDataCapacity = dataSize; mOwner = relFunc; @@ -2874,15 +2944,17 @@ void Parcel::releaseObjects() #endif // BINDER_WITH_KERNEL_IPC } -void Parcel::acquireObjects() -{ +void Parcel::reacquireObjects(size_t objectsSize) { auto* kernelFields = maybeKernelFields(); if (kernelFields == nullptr) { return; } #ifdef BINDER_WITH_KERNEL_IPC - size_t i = kernelFields->mObjectsSize; + LOG_ALWAYS_FATAL_IF(objectsSize > kernelFields->mObjectsSize, + "Object size %zu out of range of %zu", objectsSize, + kernelFields->mObjectsSize); + size_t i = objectsSize; if (i == 0) { return; } @@ -2892,8 +2964,10 @@ void Parcel::acquireObjects() while (i > 0) { i--; const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]); - acquire_object(proc, *flat, this); + acquire_object(proc, *flat, this, false /*tagFds*/); // they are already tagged } +#else + (void) objectsSize; #endif // BINDER_WITH_KERNEL_IPC } @@ -3110,12 +3184,8 @@ status_t Parcel::continueWrite(size_t desired) return NO_MEMORY; } - // Little hack to only acquire references on objects - // we will be keeping. - size_t oldObjectsSize = kernelFields->mObjectsSize; - kernelFields->mObjectsSize = objectsSize; - acquireObjects(); - kernelFields->mObjectsSize = oldObjectsSize; + // only acquire references on objects we are keeping + reacquireObjects(objectsSize); } if (rpcFields) { if (status_t status = truncateRpcObjects(objectsSize); status != OK) { diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 5e7f1510bc..0e1e9b4127 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -501,6 +501,21 @@ bool ProcessState::isThreadPoolStarted() const { return mThreadPoolStarted; } +void ProcessState::checkExpectingThreadPoolStart() const { + if (mThreadPoolStarted) return; + + // this is also racey, but you should setup the threadpool in the main thread. If that is an + // issue, we can check if we are the process leader, but haven't seen the issue in practice. + size_t requestedThreads = mMaxThreads.load(); + + // if it's manually set to the default, we do ignore it here... + if (requestedThreads == DEFAULT_MAX_BINDER_THREADS) return; + if (requestedThreads == 0) return; + + ALOGW("Thread pool configuration of size %zu requested, but startThreadPool was not called.", + requestedThreads); +} + #define DRIVER_FEATURES_PATH "/dev/binderfs/features/" bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) { // Use static variable to cache the results. diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h index 65cdcd7735..b3a2d9ec28 100644 --- a/libs/binder/binder_module.h +++ b/libs/binder/binder_module.h @@ -32,34 +32,4 @@ #include <linux/android/binder.h> #include <sys/ioctl.h> -struct binder_frozen_state_info { - binder_uintptr_t cookie; - __u32 is_frozen; -}; - -#ifndef BR_FROZEN_BINDER -// Temporary definition of BR_FROZEN_BINDER until UAPI binder.h includes it. -#define BR_FROZEN_BINDER _IOR('r', 21, struct binder_frozen_state_info) -#endif // BR_FROZEN_BINDER - -#ifndef BR_CLEAR_FREEZE_NOTIFICATION_DONE -// Temporary definition of BR_CLEAR_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it. -#define BR_CLEAR_FREEZE_NOTIFICATION_DONE _IOR('r', 22, binder_uintptr_t) -#endif // BR_CLEAR_FREEZE_NOTIFICATION_DONE - -#ifndef BC_REQUEST_FREEZE_NOTIFICATION -// Temporary definition of BC_REQUEST_FREEZE_NOTIFICATION until UAPI binder.h includes it. -#define BC_REQUEST_FREEZE_NOTIFICATION _IOW('c', 19, struct binder_handle_cookie) -#endif // BC_REQUEST_FREEZE_NOTIFICATION - -#ifndef BC_CLEAR_FREEZE_NOTIFICATION -// Temporary definition of BC_CLEAR_FREEZE_NOTIFICATION until UAPI binder.h includes it. -#define BC_CLEAR_FREEZE_NOTIFICATION _IOW('c', 20, struct binder_handle_cookie) -#endif // BC_CLEAR_FREEZE_NOTIFICATION - -#ifndef BC_FREEZE_NOTIFICATION_DONE -// Temporary definition of BC_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it. -#define BC_FREEZE_NOTIFICATION_DONE _IOW('c', 21, binder_uintptr_t) -#endif // BC_FREEZE_NOTIFICATION_DONE - #endif // _BINDER_MODULE_H_ diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index 5924a2d878..cdee17c2ee 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -219,7 +219,6 @@ namespace internal { constexpr const char* const kManualInterfaces[] = { "android.app.IActivityManager", "android.app.IUidObserver", - "android.drm.IDrm", "android.gfx.tests.ICallback", "android.gfx.tests.IIPCTest", "android.gfx.tests.ISafeInterfaceTest", @@ -233,17 +232,13 @@ constexpr const char* const kManualInterfaces[] = { "android.hardware.ICameraClient", "android.hardware.ICameraRecordingProxy", "android.hardware.ICameraRecordingProxyListener", - "android.hardware.ICrypto", "android.hardware.IOMXObserver", "android.hardware.IStreamListener", "android.hardware.IStreamSource", "android.media.IAudioService", "android.media.IDataSource", - "android.media.IDrmClient", "android.media.IMediaCodecList", - "android.media.IMediaDrmService", "android.media.IMediaExtractor", - "android.media.IMediaExtractorService", "android.media.IMediaHTTPConnection", "android.media.IMediaHTTPService", "android.media.IMediaLogService", @@ -258,9 +253,6 @@ constexpr const char* const kManualInterfaces[] = { "android.media.IMediaSource", "android.media.IRemoteDisplay", "android.media.IRemoteDisplayClient", - "android.media.IResourceManagerClient", - "android.media.IResourceManagerService", - "android.os.IComplexTypeInterface", "android.os.IPermissionController", "android.os.IProcessInfoService", "android.os.ISchedulingPolicyService", diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 0c7366e683..b4efa0af6d 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -649,6 +649,11 @@ public: LIBBINDER_EXPORTED void print(std::ostream& to, uint32_t flags = 0) const; + // This API is to quickly become a view of another Parcel, so that we can also + // test 'owner' paths quickly. It's extremely dangerous to use this API in + // practice, and you should never ever do it. + LIBBINDER_EXPORTED void makeDangerousViewOf(Parcel* p); + private: // Close all file descriptors in the parcel at object positions >= newObjectsSize. void closeFileDescriptors(size_t newObjectsSize); @@ -664,7 +669,7 @@ private: void ipcSetDataReference(const uint8_t* data, size_t dataSize, const binder_size_t* objects, size_t objectsCount, release_func relFunc); // Takes ownership even when an error is returned. - status_t rpcSetDataReference( + [[nodiscard]] status_t rpcSetDataReference( const sp<RpcSession>& session, const uint8_t* data, size_t dataSize, const uint32_t* objectTable, size_t objectTableSize, std::vector<std::variant<binder::unique_fd, binder::borrowed_fd>>&& ancillaryFds, @@ -672,7 +677,7 @@ private: status_t finishWrite(size_t len); void releaseObjects(); - void acquireObjects(); + void reacquireObjects(size_t objectSize); status_t growData(size_t len); // Clear the Parcel and set the capacity to `desired`. // Doesn't reset the RPC session association. diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index 21bfd42fcc..ced49c19c2 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -141,6 +141,8 @@ public: private: static sp<ProcessState> init(const char* defaultDriver, bool requireDefault); + void checkExpectingThreadPoolStart() const; + static void onFork(); static void parentPostFork(); static void childPostFork(); diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index a84a0c6e0b..64b1be25d2 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -23,11 +23,8 @@ #ifndef __TRUSTY__ #include <cutils/sockets.h> -#endif - -#ifdef __linux__ -#include <linux/vm_sockets.h> -#endif // __linux__ +#include "vm_sockets.h" +#endif // !__TRUSTY__ using android::OK; using android::RpcServer; diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 1b24b0a1e1..e08a76312d 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -104,7 +104,7 @@ mod proxy; mod service; #[cfg(not(any(trusty, android_ndk)))] mod state; -#[cfg(not(any(android_vendor, android_ndk, android_vndk)))] +#[cfg(not(any(android_vendor, android_ndk, android_vndk, trusty)))] mod system_only; use binder_ndk_sys as sys; @@ -125,7 +125,7 @@ pub use service::{ pub use service::{get_interface, get_service}; #[cfg(not(any(trusty, android_ndk)))] pub use state::{ProcessState, ThreadState}; -#[cfg(not(any(android_vendor, android_vndk, android_ndk)))] +#[cfg(not(any(android_vendor, android_vndk, android_ndk, trusty)))] pub use system_only::{delegate_accessor, Accessor, AccessorProvider, ConnectionInfo}; /// Binder result containing a [`Status`] on error. diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h index 2812da79fa..11fcb061f6 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h @@ -28,6 +28,9 @@ struct RandomParcelOptions { std::function<void(Parcel* p, FuzzedDataProvider& provider)> writeHeader; std::vector<sp<IBinder>> extraBinders; std::vector<binder::unique_fd> extraFds; + + // internal state owned by fillRandomParcel, for Parcel views + std::vector<std::unique_ptr<Parcel>> extraParcels; }; /** diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp index 192f9d5dce..d06b2d9020 100644 --- a/libs/binder/tests/parcel_fuzzer/main.cpp +++ b/libs/binder/tests/parcel_fuzzer/main.cpp @@ -70,7 +70,7 @@ void doTransactFuzz(const char* backend, const sp<B>& binder, FuzzedDataProvider uint32_t code = provider.ConsumeIntegral<uint32_t>(); uint32_t flag = provider.ConsumeIntegral<uint32_t>(); - FUZZ_LOG() << "backend: " << backend; + FUZZ_LOG() << "doTransactFuzz backend: " << backend; RandomParcelOptions options; @@ -101,7 +101,7 @@ void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads, // since we are only using a byte to index CHECK_LE(reads.size(), 255u) << reads.size(); - FUZZ_LOG() << "backend: " << backend; + FUZZ_LOG() << "doReadFuzz backend: " << backend; FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize()); FUZZ_LOG() << "instructions: " << HexString(instructions.data(), instructions.size()); @@ -122,10 +122,15 @@ void doReadWriteFuzz(const char* backend, const std::vector<ParcelRead<P>>& read RandomParcelOptions options; P p; + // small amount of initial Parcel data, since fillRandomParcel uses makeDangerousViewOf + std::vector<uint8_t> parcelData = + provider.ConsumeBytes<uint8_t>(provider.ConsumeIntegralInRange<size_t>(0, 20)); + fillRandomParcel(&p, FuzzedDataProvider(parcelData.data(), parcelData.size()), &options); + // since we are only using a byte to index CHECK_LE(reads.size() + writes.size(), 255u) << reads.size(); - FUZZ_LOG() << "backend: " << backend; + FUZZ_LOG() << "doReadWriteFuzz backend: " << backend; while (provider.remaining_bytes() > 0) { uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, reads.size() + writes.size() - 1); diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index 7c196142e8..dfd178a450 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -17,6 +17,7 @@ #include <fuzzbinder/random_parcel.h> #include <android-base/logging.h> +#include <binder/Functional.h> #include <binder/RpcSession.h> #include <binder/RpcTransportRaw.h> #include <fuzzbinder/random_binder.h> @@ -32,10 +33,29 @@ static void fillRandomParcelData(Parcel* p, FuzzedDataProvider&& provider) { CHECK(OK == p->write(data.data(), data.size())); } -void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOptions* options) { +void fillRandomParcel(Parcel* outputParcel, FuzzedDataProvider&& provider, + RandomParcelOptions* options) { CHECK_NE(options, nullptr); - if (provider.ConsumeBool()) { + const uint8_t fuzzerParcelOptions = provider.ConsumeIntegral<uint8_t>(); + const bool resultShouldBeView = fuzzerParcelOptions & 1; + const bool resultShouldBeRpc = fuzzerParcelOptions & 2; + + Parcel* p; + if (resultShouldBeView) { + options->extraParcels.push_back(std::make_unique<Parcel>()); + // held for duration of test, so that view will be valid + p = options->extraParcels[options->extraParcels.size() - 1].get(); + } else { + p = outputParcel; // directly fill out the output Parcel + } + auto viewify_guard = binder::impl::make_scope_guard([&]() { + if (resultShouldBeView) { + outputParcel->makeDangerousViewOf(p); + } + }); + + if (resultShouldBeRpc) { auto session = RpcSession::make(RpcTransportCtxFactoryRaw::make()); CHECK_EQ(OK, session->addNullDebuggingClient()); // Set the protocol version so that we don't crash if the session diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp index 0ed8a554ac..3cb628925c 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp @@ -281,9 +281,9 @@ void generateSeedsFromRecording(borrowed_fd fd, // This buffer holds the bytes which will be used for fillRandomParcel API std::vector<uint8_t> fillParcelBuffer; - // Don't take rpc path - uint8_t rpcBranch = 0; - impl::writeReversedBuffer(fillParcelBuffer, rpcBranch); + // Use all default options. + uint8_t parcelOptions = 0; + impl::writeReversedBuffer(fillParcelBuffer, parcelOptions); // Implicit branch on this path -> options->writeHeader(p, provider) uint8_t writeHeaderInternal = 0; diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk index 5e38ad0d0c..dd9d4d12ee 100644 --- a/libs/binder/trusty/rules.mk +++ b/libs/binder/trusty/rules.mk @@ -64,14 +64,24 @@ MODULE_EXPORT_INCLUDES += \ MODULE_EXPORT_INCLUDES += \ $(LIBBINDER_DIR)/ndk/include_cpp \ +ifeq (false,$(call TOBOOL,$(USE_SYSTEM_BINDER))) +BINDER_EXTRA_COMPILE_FLAGS := \ + -D__ANDROID_VENDOR__ \ + -D__ANDROID_VNDK__ \ + +else +BINDER_EXTRA_COMPILE_FLAGS := \ + -DANDROID_PLATFORM \ + +endif + MODULE_EXPORT_COMPILEFLAGS += \ -DBINDER_RPC_SINGLE_THREADED \ -DBINDER_ENABLE_LIBLOG_ASSERT \ -DBINDER_DISABLE_NATIVE_HANDLE \ -DBINDER_DISABLE_BLOB \ -DBINDER_NO_LIBBASE \ - -D__ANDROID_VENDOR__ \ - -D__ANDROID_VNDK__ \ + $(BINDER_EXTRA_COMPILE_FLAGS) # libbinder has some deprecated declarations that we want to produce warnings # not errors diff --git a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk index 2aaa061ddf..56d711efae 100644 --- a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk +++ b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk @@ -30,9 +30,14 @@ MODULE_LIBRARY_DEPS += \ trusty/user/base/lib/trusty-sys \ MODULE_RUSTFLAGS += \ - --cfg 'android_vendor' \ --cfg 'trusty' \ +ifeq (false,$(call TOBOOL,$(USE_SYSTEM_BINDER))) +MODULE_RUSTFLAGS += \ + --cfg 'android_vendor' \ + +endif + MODULE_BINDGEN_SRC_HEADER := $(LIBBINDER_DIR)/rust/sys/BinderBindings.hpp # Add the flags from the flag file diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk index e622b22246..acd74d2c86 100644 --- a/libs/binder/trusty/rust/rules.mk +++ b/libs/binder/trusty/rust/rules.mk @@ -32,9 +32,15 @@ MODULE_LIBRARY_DEPS += \ trusty/user/base/lib/trusty-sys \ MODULE_RUSTFLAGS += \ - --cfg 'android_vendor' \ --cfg 'trusty' \ +ifeq (false,$(call TOBOOL,$(USE_SYSTEM_BINDER))) +MODULE_RUSTFLAGS += \ + --cfg 'android_vendor' \ + +endif + + # Trusty does not have `ProcessState`, so there are a few # doc links in `IBinder` that are still broken. MODULE_RUSTFLAGS += \ diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 7aee90393b..38465b0de2 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -1032,7 +1032,7 @@ void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transacti std::lock_guard _lock{mMutex}; if (mLastAcquiredFrameNumber >= frameNumber) { // Apply the transaction since we have already acquired the desired frame. - t->apply(); + t->setApplyToken(mApplyToken).apply(); } else { mPendingTransactions.emplace_back(frameNumber, *t); // Clear the transaction so it can't be applied elsewhere. @@ -1232,6 +1232,11 @@ public: return OK; } + // Provide a callback for Choreographer to start buffer stuffing recovery when blocked + // on buffer release. + std::function<void()> callbackCopy = bbq->getWaitForBufferReleaseCallback(); + if (callbackCopy) callbackCopy(); + // BufferQueue has already checked if we have a free buffer. If there's an unread interrupt, // we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure // we don't miss an interrupt. @@ -1344,6 +1349,16 @@ void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { mApplyToken = std::move(applyToken); } +void BLASTBufferQueue::setWaitForBufferReleaseCallback(std::function<void()> callback) { + std::lock_guard _lock{mWaitForBufferReleaseMutex}; + mWaitForBufferReleaseCallback = std::move(callback); +} + +std::function<void()> BLASTBufferQueue::getWaitForBufferReleaseCallback() const { + std::lock_guard _lock{mWaitForBufferReleaseMutex}; + return mWaitForBufferReleaseCallback; +} + #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) void BLASTBufferQueue::updateBufferReleaseProducer() { diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index b0f6e69115..f1374e23fd 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -108,6 +108,15 @@ void BufferQueue::ProxyConsumerListener::onSetFrameRate(float frameRate, int8_t } #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +void BufferQueue::ProxyConsumerListener::onSlotCountChanged(int slotCount) { + sp<ConsumerListener> listener(mConsumerListener.promote()); + if (listener != nullptr) { + listener->onSlotCountChanged(slotCount); + } +} +#endif + void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer, bool consumerIsSurfaceFlinger) { diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index 9855b5bca4..f0125868ae 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -341,9 +341,9 @@ status_t BufferQueueConsumer::detachBuffer(int slot) { return BAD_VALUE; } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", - slot, BufferQueueDefs::NUM_BUFFER_SLOTS); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount) { + BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isAcquired()) { BQ_LOGE("detachBuffer: slot %d is not owned by the consumer " @@ -483,10 +483,13 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS || - releaseFence == nullptr) { - BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot, - releaseFence.get()); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount) { + BQ_LOGE("releaseBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount); + return BAD_VALUE; + } + if (releaseFence == nullptr) { + BQ_LOGE("releaseBuffer: slot %d fence %p NULL", slot, releaseFence.get()); return BAD_VALUE; } @@ -515,6 +518,13 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, { // Autolock scope std::lock_guard<std::mutex> lock(mCore->mMutex); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount || releaseFence == nullptr) { + BQ_LOGE("releaseBuffer: slot %d out of range [0, %d) or fence %p NULL", slot, + totalSlotCount, releaseFence.get()); + return BAD_VALUE; + } + // If the frame number has changed because the buffer has been reallocated, // we can ignore this releaseBuffer for the old buffer. // Ignore this for the shared buffer where the frame number can easily @@ -661,6 +671,43 @@ status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) { return NO_ERROR; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +status_t BufferQueueConsumer::getReleasedBuffersExtended(std::vector<bool>* outSlotMask) { + ATRACE_CALL(); + + if (outSlotMask == nullptr) { + BQ_LOGE("getReleasedBuffersExtended: outSlotMask may not be NULL"); + return BAD_VALUE; + } + + std::lock_guard<std::mutex> lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + BQ_LOGE("getReleasedBuffersExtended: BufferQueue has been abandoned"); + return NO_INIT; + } + + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + outSlotMask->resize(totalSlotCount); + for (int s = 0; s < totalSlotCount; ++s) { + (*outSlotMask)[s] = !mSlots[s].mAcquireCalled; + } + + // Remove from the mask queued buffers for which acquire has been called, + // since the consumer will not receive their buffer addresses and so must + // retain their cached information + BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin()); + while (current != mCore->mQueue.end()) { + if (current->mAcquireCalled) { + (*outSlotMask)[current->mSlot] = false; + } + ++current; + } + + return NO_ERROR; +} +#endif + status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width, uint32_t height) { ATRACE_CALL(); @@ -679,6 +726,28 @@ status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width, return NO_ERROR; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +status_t BufferQueueConsumer::allowUnlimitedSlots(bool allowUnlimitedSlots) { + ATRACE_CALL(); + BQ_LOGV("allowUnlimitedSlots: %d", allowUnlimitedSlots); + std::lock_guard<std::mutex> lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + BQ_LOGE("allowUnlimitedSlots: BufferQueue has been abandoned"); + return NO_INIT; + } + + if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { + BQ_LOGE("allowUnlimitedSlots: BufferQueue already connected"); + return INVALID_OPERATION; + } + + mCore->mAllowExtendedSlotCount = allowUnlimitedSlots; + + return OK; +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + status_t BufferQueueConsumer::setMaxBufferCount(int bufferCount) { ATRACE_CALL(); @@ -718,16 +787,23 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( int maxAcquiredBuffers) { ATRACE_FORMAT("%s(%d)", __func__, maxAcquiredBuffers); - if (maxAcquiredBuffers < 1 || - maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) { - BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d", - maxAcquiredBuffers); - return BAD_VALUE; - } - sp<IConsumerListener> listener; { // Autolock scope std::unique_lock<std::mutex> lock(mCore->mMutex); + + // We reserve two slots in order to guarantee that the producer and + // consumer can run asynchronously. + int maxMaxAcquiredBuffers = +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mCore->getTotalSlotCountLocked() - 2; +#else + BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS; +#endif + if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > maxMaxAcquiredBuffers) { + BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d", maxAcquiredBuffers); + return BAD_VALUE; + } + mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index 5a093995b4..6c79904475 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -38,6 +38,8 @@ #include <system/window.h> +#include <ui/BufferQueueDefs.h> + namespace android { // Macros for include BufferQueueCore information in log messages @@ -97,7 +99,11 @@ BufferQueueCore::BufferQueueCore() mConnectedProducerListener(), mBufferReleasedCbEnabled(false), mBufferAttachedCbEnabled(false), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#else mSlots(), +#endif mQueue(), mFreeSlots(), mFreeBuffers(), @@ -111,6 +117,9 @@ BufferQueueCore::BufferQueueCore() mDefaultWidth(1), mDefaultHeight(1), mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mAllowExtendedSlotCount(false), +#endif mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS), mMaxAcquiredBufferCount(1), mMaxDequeuedBufferCount(1), @@ -221,6 +230,14 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const } } +int BufferQueueCore::getTotalSlotCountLocked() const { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + return mAllowExtendedSlotCount ? mMaxBufferCount : BufferQueueDefs::NUM_BUFFER_SLOTS; +#else + return BufferQueueDefs::NUM_BUFFER_SLOTS; +#endif +} + int BufferQueueCore::getMinUndequeuedBufferCountLocked() const { // If dequeueBuffer is allowed to error out, we don't have to add an // extra buffer. @@ -253,6 +270,26 @@ int BufferQueueCore::getMaxBufferCountLocked() const { return maxBufferCount; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +status_t BufferQueueCore::extendSlotCountLocked(int size) { + int previousSize = (int)mSlots.size(); + if (previousSize > size) { + return BAD_VALUE; + } + if (previousSize == size) { + return NO_ERROR; + } + + mSlots.resize(size); + for (int i = previousSize; i < size; i++) { + mUnusedSlots.push_back(i); + } + + mMaxBufferCount = size; + return NO_ERROR; +} +#endif + void BufferQueueCore::clearBufferSlotLocked(int slot) { BQ_LOGV("clearBufferSlotLocked: slot %d", slot); @@ -383,7 +420,7 @@ void BufferQueueCore::notifyBufferReleased() const { void BufferQueueCore::validateConsistencyLocked() const { static const useconds_t PAUSE_TIME = 0; int allocatedSlots = 0; - for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + for (int slot = 0; slot < getTotalSlotCountLocked(); ++slot) { bool isInFreeSlots = mFreeSlots.count(slot) != 0; bool isInFreeBuffers = std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) != diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 2e7cef0847..c241482827 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -40,6 +40,7 @@ #include <gui/TraceUtils.h> #include <private/gui/BufferQueueThreadState.h> +#include <utils/Errors.h> #include <utils/Log.h> #include <utils/Trace.h> @@ -108,9 +109,9 @@ status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { return NO_INIT; } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", - slot, BufferQueueDefs::NUM_BUFFER_SLOTS); + int maxSlot = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= maxSlot) { + BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", slot, maxSlot); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("requestBuffer: slot %d is not owned by the producer " @@ -123,6 +124,49 @@ status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { return NO_ERROR; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +status_t BufferQueueProducer::extendSlotCount(int size) { + ATRACE_CALL(); + + sp<IConsumerListener> listener; + { + std::lock_guard<std::mutex> lock(mCore->mMutex); + BQ_LOGV("extendSlotCount: size %d", size); + + if (mCore->mIsAbandoned) { + BQ_LOGE("extendSlotCount: BufferQueue has been abandoned"); + return NO_INIT; + } + + if (!mCore->mAllowExtendedSlotCount) { + BQ_LOGE("extendSlotCount: Consumer did not allow unlimited slots"); + return INVALID_OPERATION; + } + + int maxBeforeExtension = mCore->mMaxBufferCount; + + if (size == maxBeforeExtension) { + return NO_ERROR; + } + + if (size < maxBeforeExtension) { + return BAD_VALUE; + } + + if (status_t ret = mCore->extendSlotCountLocked(size); ret != OK) { + return ret; + } + listener = mCore->mConsumerListener; + } + + if (listener) { + listener->onSlotCountChanged(size); + } + + return NO_ERROR; +} +#endif + status_t BufferQueueProducer::setMaxDequeuedBufferCount( int maxDequeuedBuffers) { int maxBufferCount; @@ -170,9 +214,10 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount(int maxDequeuedBuffers, int bufferCount = mCore->getMinUndequeuedBufferCountLocked(); bufferCount += maxDequeuedBuffers; - if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) { + if (bufferCount > mCore->getTotalSlotCountLocked()) { BQ_LOGE("setMaxDequeuedBufferCount: bufferCount %d too large " - "(max %d)", bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS); + "(max %d)", + bufferCount, mCore->getTotalSlotCountLocked()); return BAD_VALUE; } @@ -756,9 +801,9 @@ status_t BufferQueueProducer::detachBuffer(int slot) { return BAD_VALUE; } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", - slot, BufferQueueDefs::NUM_BUFFER_SLOTS); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount) { + BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { // TODO(http://b/140581935): This message is BQ_LOGW because it @@ -993,9 +1038,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, return NO_INIT; } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", - slot, BufferQueueDefs::NUM_BUFFER_SLOTS); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount) { + BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("queueBuffer: slot %d is not owned by the producer " @@ -1239,9 +1284,9 @@ status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) { return BAD_VALUE; } - if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, - BufferQueueDefs::NUM_BUFFER_SLOTS); + const int totalSlotCount = mCore->getTotalSlotCountLocked(); + if (slot < 0 || slot >= totalSlotCount) { + BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, totalSlotCount); return BAD_VALUE; } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " @@ -1409,6 +1454,9 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, output->nextFrameNumber = mCore->mFrameCounter + 1; output->bufferReplaced = false; output->maxBufferCount = mCore->mMaxBufferCount; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + output->isSlotExpansionAllowed = mCore->mAllowExtendedSlotCount; +#endif if (listener != nullptr) { // Set up a death notification so that we can disconnect diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 602bba8dab..504509dbec 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -37,6 +37,8 @@ #include <private/gui/ComposerService.h> +#include <ui/BufferQueueDefs.h> + #include <log/log.h> #include <utils/Log.h> #include <utils/String8.h> @@ -59,7 +61,11 @@ static int32_t createProcessUniqueId() { return android_atomic_inc(&globalCounter); } -ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) : +ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) + : +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif mAbandoned(false), mConsumer(bufferQueue), mPrevFinalReleaseFence(Fence::NO_FENCE) { @@ -68,7 +74,12 @@ ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool c #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger) - : mAbandoned(false), mPrevFinalReleaseFence(Fence::NO_FENCE) { + : +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif + mAbandoned(false), + mPrevFinalReleaseFence(Fence::NO_FENCE) { sp<IGraphicBufferProducer> producer; BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger); mSurface = sp<Surface>::make(producer, controlledByApp); @@ -77,7 +88,11 @@ ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger) ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer, const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp) - : mAbandoned(false), + : +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif + mAbandoned(false), mConsumer(consumer), mSurface(sp<Surface>::make(producer, controlledByApp)), mPrevFinalReleaseFence(Fence::NO_FENCE) { @@ -101,9 +116,16 @@ void ConsumerBase::initialize(bool controlledByApp) { if (err != NO_ERROR) { CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)", strerror(-err), err); - } else { - mConsumer->setConsumerName(mName); + return; } + + mConsumer->setConsumerName(mName); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (err = mConsumer->allowUnlimitedSlots(true); err != NO_ERROR) { + CB_LOGE("ConsumerBase: error marking as allowed to have unlimited slots: %s (%d)", + strerror(-err), err); + } +#endif } ConsumerBase::~ConsumerBase() { @@ -130,7 +152,11 @@ int ConsumerBase::getSlotForBufferLocked(const sp<GraphicBuffer>& buffer) { } uint64_t id = buffer->getId(); - for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); ++i) { +#else + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { +#endif auto& slot = mSlots[i]; if (slot.mGraphicBuffer && slot.mGraphicBuffer->getId() == id) { return i; @@ -242,6 +268,15 @@ void ConsumerBase::onBuffersReleased() { return; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + std::vector<bool> mask; + mConsumer->getReleasedBuffersExtended(&mask); + for (size_t i = 0; i < mSlots.size(); i++) { + if (mask[i]) { + freeBufferLocked(i); + } + } +#else uint64_t mask = 0; mConsumer->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { @@ -249,11 +284,23 @@ void ConsumerBase::onBuffersReleased() { freeBufferLocked(i); } } +#endif } void ConsumerBase::onSidebandStreamChanged() { } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +void ConsumerBase::onSlotCountChanged(int slotCount) { + CB_LOGV("onSlotCountChanged: %d", slotCount); + Mutex::Autolock lock(mMutex); + + if (slotCount > (int)mSlots.size()) { + mSlots.resize(slotCount); + } +} +#endif + void ConsumerBase::abandon() { CB_LOGV("abandon"); Mutex::Autolock lock(mMutex); @@ -270,7 +317,11 @@ void ConsumerBase::abandonLocked() { CB_LOGE("abandonLocked: ConsumerBase is abandoned!"); return; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); ++i) { +#else for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { +#endif freeBufferLocked(i); } // disconnect from the BufferQueue @@ -387,6 +438,15 @@ status_t ConsumerBase::setMaxBufferCount(int bufferCount) { CB_LOGE("setMaxBufferCount: ConsumerBase is abandoned!"); return NO_INIT; } + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (status_t err = mConsumer->allowUnlimitedSlots(false); err != NO_ERROR) { + CB_LOGE("ConsumerBase: error marking as not allowed to have unlimited slots: %s (%d)", + strerror(-err), err); + return err; + } +#endif + return mConsumer->setMaxBufferCount(bufferCount); } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) @@ -448,6 +508,15 @@ status_t ConsumerBase::discardFreeBuffers() { if (err != OK) { return err; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + std::vector<bool> mask; + mConsumer->getReleasedBuffersExtended(&mask); + for (int i = 0; i < (int)mSlots.size(); i++) { + if (mask[i]) { + freeBufferLocked(i); + } + } +#else uint64_t mask; mConsumer->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { @@ -455,6 +524,8 @@ status_t ConsumerBase::discardFreeBuffers() { freeBufferLocked(i); } } +#endif + return OK; } @@ -596,6 +667,9 @@ status_t ConsumerBase::releaseBufferLocked( // buffer on the same slot), the buffer producer is definitely no longer // tracking it. if (!stillTracking(slot, graphicBuffer)) { + CB_LOGV("releaseBufferLocked: Not tracking, exiting without calling releaseBuffer for " + "slot=%d/%" PRIu64, + slot, mSlots[slot].mFrameNumber); return OK; } @@ -615,7 +689,11 @@ status_t ConsumerBase::releaseBufferLocked( bool ConsumerBase::stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (slot < 0 || slot >= (int)mSlots.size()) { +#else if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) { +#endif return false; } return (mSlots[slot].mGraphicBuffer != nullptr && diff --git a/libs/gui/Flags.cpp b/libs/gui/Flags.cpp index 85ee2cddad..ee2802f706 100644 --- a/libs/gui/Flags.cpp +++ b/libs/gui/Flags.cpp @@ -29,6 +29,14 @@ sp<SurfaceType> surfaceToSurfaceType(const sp<Surface>& surface) { #endif } +ParcelableSurfaceType surfaceToParcelableSurfaceType(const sp<Surface>& surface) { +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + return view::Surface::fromSurface(surface); +#else + return surface->getIGraphicBufferProducer(); +#endif +} + sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface) { #if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES return surface->getIGraphicBufferProducer(); diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp index 5c4879c1bd..1b2354e942 100644 --- a/libs/gui/FrameRateUtils.cpp +++ b/libs/gui/FrameRateUtils.cpp @@ -42,7 +42,7 @@ bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrame if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT && compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE && - compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE && + compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST && (!privileged || (compatibility != ANATIVEWINDOW_FRAME_RATE_EXACT && compatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE))) { diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index f2173cd740..168129b1f7 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -119,6 +119,9 @@ GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool mTexTarget(texTarget), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(true) { GLC_LOGV("GLConsumer"); @@ -129,27 +132,29 @@ GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) -GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, - uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : - ConsumerBase(bq, isControlledByApp), - mCurrentCrop(Rect::EMPTY_RECT), - mCurrentTransform(0), - mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mCurrentFence(Fence::NO_FENCE), - mCurrentTimestamp(0), - mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), - mCurrentFrameNumber(0), - mDefaultWidth(1), - mDefaultHeight(1), - mFilteringEnabled(true), - mTexName(tex), - mUseFenceSync(useFenceSync), - mTexTarget(texTarget), - mEglDisplay(EGL_NO_DISPLAY), - mEglContext(EGL_NO_CONTEXT), - mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), - mAttached(true) -{ +GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texTarget, + bool useFenceSync, bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp), + mCurrentCrop(Rect::EMPTY_RECT), + mCurrentTransform(0), + mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mCurrentFence(Fence::NO_FENCE), + mCurrentTimestamp(0), + mCurrentDataSpace(HAL_DATASPACE_UNKNOWN), + mCurrentFrameNumber(0), + mDefaultWidth(1), + mDefaultHeight(1), + mFilteringEnabled(true), + mTexName(tex), + mUseFenceSync(useFenceSync), + mTexTarget(texTarget), + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif + mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), + mAttached(true) { GLC_LOGV("GLConsumer"); memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), @@ -176,6 +181,9 @@ GLConsumer::GLConsumer(uint32_t texTarget, bool useFenceSync, bool isControlledB mTexTarget(texTarget), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(false) { GLC_LOGV("GLConsumer"); @@ -204,6 +212,9 @@ GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, mTexTarget(texTarget), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mEglSlots(BufferQueueDefs::NUM_BUFFER_SLOTS), +#endif mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(false) { GLC_LOGV("GLConsumer"); @@ -395,6 +406,17 @@ status_t GLConsumer::acquireBufferLocked(BufferItem *item, return NO_ERROR; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +void GLConsumer::onSlotCountChanged(int slotCount) { + ConsumerBase::onSlotCountChanged(slotCount); + + Mutex::Autolock lock(mMutex); + if (slotCount > (int)mEglSlots.size()) { + mEglSlots.resize(slotCount); + } +} +#endif + status_t GLConsumer::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer, EGLDisplay display, EGLSyncKHR eglFence) { diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp index f3bd90cffb..939db594ec 100644 --- a/libs/gui/IConsumerListener.cpp +++ b/libs/gui/IConsumerListener.cpp @@ -31,7 +31,8 @@ enum class Tag : uint32_t { ON_FRAME_DEQUEUED, ON_FRAME_CANCELLED, ON_FRAME_DETACHED, - LAST = ON_FRAME_DETACHED, + ON_SLOT_COUNT_CHANGED, + LAST = ON_SLOT_COUNT_CHANGED, }; } // Anonymous namespace @@ -85,6 +86,14 @@ public: FrameEventHistoryDelta* /*outDelta*/) override { LOG_ALWAYS_FATAL("IConsumerListener::addAndGetFrameTimestamps cannot be proxied"); } + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + void onSlotCountChanged(int slotCount) override { + callRemoteAsync< + decltype(&IConsumerListener::onSlotCountChanged)>(Tag::ON_SLOT_COUNT_CHANGED, + slotCount); + } +#endif }; // Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see @@ -116,6 +125,13 @@ status_t BnConsumerListener::onTransact(uint32_t code, const Parcel& data, Parce return callLocalAsync(data, reply, &IConsumerListener::onFrameCancelled); case Tag::ON_FRAME_DETACHED: return callLocalAsync(data, reply, &IConsumerListener::onFrameDetached); + case Tag::ON_SLOT_COUNT_CHANGED: { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + return callLocalAsync(data, reply, &IConsumerListener::onSlotCountChanged); +#else + return INVALID_OPERATION; +#endif + } } } diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp index 282957b940..c1b65689d6 100644 --- a/libs/gui/IGraphicBufferConsumer.cpp +++ b/libs/gui/IGraphicBufferConsumer.cpp @@ -16,6 +16,7 @@ #include <gui/IGraphicBufferConsumer.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferItem.h> #include <gui/IConsumerListener.h> @@ -24,6 +25,7 @@ #include <ui/Fence.h> #include <ui/GraphicBuffer.h> +#include <utils/Errors.h> #include <utils/NativeHandle.h> #include <utils/String8.h> #include <cstdint> @@ -53,7 +55,9 @@ enum class Tag : uint32_t { GET_OCCUPANCY_HISTORY, DISCARD_FREE_BUFFERS, DUMP_STATE, - LAST = DUMP_STATE, + ALLOW_UNLIMITED_SLOTS, + GET_RELEASED_BUFFERS_EXTENDED, + LAST = GET_RELEASED_BUFFERS_EXTENDED, }; } // Anonymous namespace @@ -104,11 +108,25 @@ public: return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS, slotMask); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + status_t getReleasedBuffersExtended(std::vector<bool>* slotMask) override { + using Signature = decltype(&IGraphicBufferConsumer::getReleasedBuffersExtended); + return callRemote<Signature>(Tag::GET_RELEASED_BUFFERS_EXTENDED, slotMask); + } +#endif + status_t setDefaultBufferSize(uint32_t width, uint32_t height) override { using Signature = decltype(&IGraphicBufferConsumer::setDefaultBufferSize); return callRemote<Signature>(Tag::SET_DEFAULT_BUFFER_SIZE, width, height); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + status_t allowUnlimitedSlots(bool allowUnlimitedSlots) override { + using Signature = decltype(&IGraphicBufferConsumer::allowUnlimitedSlots); + return callRemote<Signature>(Tag::ALLOW_UNLIMITED_SLOTS, allowUnlimitedSlots); + } +#endif + status_t setMaxBufferCount(int bufferCount) override { using Signature = decltype(&IGraphicBufferConsumer::setMaxBufferCount); return callRemote<Signature>(Tag::SET_MAX_BUFFER_COUNT, bufferCount); @@ -228,6 +246,20 @@ status_t BnGraphicBufferConsumer::onTransact(uint32_t code, const Parcel& data, using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const; return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState); } + case Tag::GET_RELEASED_BUFFERS_EXTENDED: { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + return callLocal(data, reply, &IGraphicBufferConsumer::getReleasedBuffersExtended); +#else + return INVALID_OPERATION; +#endif + } + case Tag::ALLOW_UNLIMITED_SLOTS: { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + return callLocal(data, reply, &IGraphicBufferConsumer::allowUnlimitedSlots); +#else + return INVALID_OPERATION; +#endif + } } } diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 09144806ee..9f71eb16e7 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -81,6 +81,7 @@ enum { GET_LAST_QUEUED_BUFFER2, SET_FRAME_RATE, SET_ADDITIONAL_OPTIONS, + SET_MAX_BUFER_COUNT_EXTENDED, }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -149,6 +150,20 @@ public: return result; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + status_t extendSlotCount(int size) override { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(size); + status_t result = remote()->transact(SET_MAX_BUFER_COUNT_EXTENDED, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32(); + return result; + } +#endif + virtual status_t setAsyncMode(bool async) { Parcel data, reply; data.writeInterfaceToken( @@ -981,6 +996,14 @@ IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, // ---------------------------------------------------------------------- +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +status_t IGraphicBufferProducer::extendSlotCount(int size) { + // No-op for IGBP other than BufferQueue. + (void)size; + return INVALID_OPERATION; +} +#endif + status_t IGraphicBufferProducer::setLegacyBufferDrop(bool drop) { // No-op for IGBP other than BufferQueue. (void) drop; @@ -1582,6 +1605,15 @@ status_t BnGraphicBufferProducer::onTransact( return NO_ERROR; } #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + case SET_MAX_BUFER_COUNT_EXTENDED: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + int size = data.readInt32(); + status_t result = extendSlotCount(size); + reply->writeInt32(result); + return NO_ERROR; + } +#endif } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp index 4e92a39973..8b2e2ddc59 100644 --- a/libs/gui/IGraphicBufferProducerFlattenables.cpp +++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp @@ -128,7 +128,7 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten( constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() { return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) + sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount) + - sizeof(result); + sizeof(result) + sizeof(isSlotExpansionAllowed); } size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const { return minFlattenedSize() + frameTimestamps.getFlattenedSize(); @@ -152,6 +152,7 @@ status_t IGraphicBufferProducer::QueueBufferOutput::flatten( FlattenableUtils::write(buffer, size, nextFrameNumber); FlattenableUtils::write(buffer, size, bufferReplaced); FlattenableUtils::write(buffer, size, maxBufferCount); + FlattenableUtils::write(buffer, size, isSlotExpansionAllowed); status_t result = frameTimestamps.flatten(buffer, size, fds, count); if (result != NO_ERROR) { @@ -175,6 +176,7 @@ status_t IGraphicBufferProducer::QueueBufferOutput::unflatten( FlattenableUtils::read(buffer, size, nextFrameNumber); FlattenableUtils::read(buffer, size, bufferReplaced); FlattenableUtils::read(buffer, size, maxBufferCount); + FlattenableUtils::read(buffer, size, isSlotExpansionAllowed); status_t result = frameTimestamps.unflatten(buffer, size, fds, count); if (result != NO_ERROR) { diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index e41f9bbf43..ec23365e1f 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -38,6 +38,7 @@ #include <utils/NativeHandle.h> #include <utils/Trace.h> +#include <ui/BufferQueueDefs.h> #include <ui/DynamicDisplayInfo.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> @@ -98,7 +99,10 @@ Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controll : mGraphicBufferProducer(bufferProducer), #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) mSurfaceDeathListener(nullptr), -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +#endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mSlots(NUM_BUFFER_SLOTS), +#endif mCrop(Rect::EMPTY_RECT), mBufferAge(0), mGenerationNumber(0), @@ -192,7 +196,7 @@ void Surface::allocateBuffers() { status_t Surface::allowAllocation(bool allowAllocation) { return mGraphicBufferProducer->allowAllocation(allowAllocation); } -#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +#endif status_t Surface::setGenerationNumber(uint32_t generation) { status_t result = mGraphicBufferProducer->setGenerationNumber(generation); @@ -658,7 +662,11 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { return result; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (buf < 0 || buf >= (int)mSlots.size()) { +#else if (buf < 0 || buf >= NUM_BUFFER_SLOTS) { +#endif ALOGE("dequeueBuffer: IGraphicBufferProducer returned invalid slot number %d", buf); android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging return FAILED_TRANSACTION; @@ -757,7 +765,11 @@ status_t Surface::detachBuffer(const sp<GraphicBuffer>& buffer) { Mutex::Autolock lock(mMutex); uint64_t bufferId = buffer->getId(); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int slot = 0; slot < (int)mSlots.size(); ++slot) { +#else for (int slot = 0; slot < Surface::NUM_BUFFER_SLOTS; ++slot) { +#endif auto& bufferSlot = mSlots[slot]; if (bufferSlot.buffer != nullptr && bufferSlot.buffer->getId() == bufferId) { bufferSlot.buffer = nullptr; @@ -840,7 +852,11 @@ int Surface::dequeueBuffers(std::vector<BatchBuffer>* buffers) { return output.result; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (output.slot < 0 || output.slot >= (int)mSlots.size()) { +#else if (output.slot < 0 || output.slot >= NUM_BUFFER_SLOTS) { +#endif mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); ALOGE("%s: IGraphicBufferProducer returned invalid slot number %d", __FUNCTION__, output.slot); @@ -1027,7 +1043,11 @@ int Surface::getSlotFromBufferLocked( return BAD_VALUE; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); i++) { +#else for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { +#endif if (mSlots[i].buffer != nullptr && mSlots[i].buffer->handle == buffer->handle) { return i; @@ -2094,6 +2114,9 @@ int Surface::connect(int api, const sp<SurfaceListener>& listener, bool reportBu mDefaultHeight = output.height; mNextFrameNumber = output.nextFrameNumber; mMaxBufferCount = output.maxBufferCount; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + mIsSlotExpansionAllowed = output.isSlotExpansionAllowed; +#endif // Ignore transform hint if sticky transform is set or transform to display inverse flag is // set. Transform hint should be ignored if the client is expected to always submit buffers @@ -2190,7 +2213,11 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, *outFence = Fence::NO_FENCE; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); i++) { +#else for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { +#endif if (mSlots[i].buffer != nullptr && mSlots[i].buffer->getId() == buffer->getId()) { if (mReportRemovedBuffers) { @@ -2292,8 +2319,35 @@ int Surface::setMaxDequeuedBufferCount(int maxDequeuedBuffers) { ALOGV("Surface::setMaxDequeuedBufferCount"); Mutex::Autolock lock(mMutex); - status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount( - maxDequeuedBuffers); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (maxDequeuedBuffers > BufferQueueDefs::NUM_BUFFER_SLOTS && !mIsSlotExpansionAllowed) { + return BAD_VALUE; + } + + int minUndequeuedBuffers = 0; + status_t err = mGraphicBufferProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &minUndequeuedBuffers); + if (err != OK) { + ALOGE("IGraphicBufferProducer::query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS) returned %s", + strerror(-err)); + return err; + } + + if (maxDequeuedBuffers > (int)mSlots.size()) { + int newSlotCount = minUndequeuedBuffers + maxDequeuedBuffers; + err = mGraphicBufferProducer->extendSlotCount(newSlotCount); + if (err != OK) { + ALOGE("IGraphicBufferProducer::extendSlotCount(%d) returned %s", newSlotCount, + strerror(-err)); + return err; + } + + mSlots.resize(newSlotCount); + } + err = mGraphicBufferProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers); +#else + status_t err = mGraphicBufferProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers); +#endif ALOGE_IF(err, "IGraphicBufferProducer::setMaxDequeuedBufferCount(%d) " "returned %s", maxDequeuedBuffers, strerror(-err)); @@ -2501,7 +2555,11 @@ void Surface::freeAllBuffers() { ALOGE("%s: %zu buffers were freed while being dequeued!", __FUNCTION__, mDequeuedSlots.size()); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); i++) { +#else for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { +#endif mSlots[i].buffer = nullptr; } } @@ -2510,7 +2568,11 @@ status_t Surface::getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots, std::vector<sp<GraphicBuffer>>* outBuffers) { ALOGV("Surface::getAndFlushBuffersFromSlots"); for (int32_t i : slots) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + if (i < 0 || i >= (int)mSlots.size()) { +#else if (i < 0 || i >= NUM_BUFFER_SLOTS) { +#endif ALOGE("%s: Invalid slotIndex: %d", __FUNCTION__, i); return BAD_VALUE; } @@ -2670,7 +2732,11 @@ status_t Surface::lock( newDirtyRegion.set(bounds); mDirtyRegion.clear(); Mutex::Autolock lock(mMutex); - for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + for (int i = 0; i < (int)mSlots.size(); i++) { +#else + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { +#endif mSlots[i].dirtyRegion.clear(); } } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index be88b11b9c..cabde22c6d 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -3293,10 +3293,17 @@ status_t SurfaceComposerClient::removeHdrLayerInfoListener( return statusTFromBinderStatus(status); } -status_t SurfaceComposerClient::setActivePictureListener( +status_t SurfaceComposerClient::addActivePictureListener( const sp<gui::IActivePictureListener>& listener) { binder::Status status = - ComposerServiceAIDL::getComposerService()->setActivePictureListener(listener); + ComposerServiceAIDL::getComposerService()->addActivePictureListener(listener); + return statusTFromBinderStatus(status); +} + +status_t SurfaceComposerClient::removeActivePictureListener( + const sp<gui::IActivePictureListener>& listener) { + binder::Status status = + ComposerServiceAIDL::getComposerService()->removeActivePictureListener(listener); return statusTFromBinderStatus(status); } diff --git a/libs/gui/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl index 4920344e0e..2bbed2b9d6 100644 --- a/libs/gui/aidl/android/gui/CaptureArgs.aidl +++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl @@ -69,10 +69,5 @@ parcelable CaptureArgs { // exact colorspace is not an appropriate intermediate result. // Note that if the caller is requesting a specific dataspace, this hint does nothing. boolean hintForSeamlessTransition = false; - - // Allows the screenshot to attach a gainmap, which allows for a per-pixel - // transformation of the screenshot to another luminance range, typically - // mapping an SDR base image into HDR. - boolean attachGainmap = false; } diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index 8c19bbbba9..da47ee27ba 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -607,8 +607,14 @@ interface ISurfaceComposer { oneway void removeJankListener(int layerId, IJankListener listener, long afterVsync); /** - * Sets the listener used to monitor visible content that is being processed with picture + * Adds a listener used to monitor visible content that is being processed with picture * profiles. */ - oneway void setActivePictureListener(IActivePictureListener listener); + oneway void addActivePictureListener(IActivePictureListener listener); + + /** + * Removes a listener used to monitor visible content that is being processed with picture + * profiles. + */ + oneway void removeActivePictureListener(IActivePictureListener listener); } diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 07558aa49d..1bc1dd0685 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -144,6 +144,12 @@ public: */ void setTransactionHangCallback(std::function<void(const std::string&)> callback); void setApplyToken(sp<IBinder>); + + void setWaitForBufferReleaseCallback(std::function<void()> callback) + EXCLUDES(mWaitForBufferReleaseMutex); + std::function<void()> getWaitForBufferReleaseCallback() const + EXCLUDES(mWaitForBufferReleaseMutex); + virtual ~BLASTBufferQueue(); void onFirstRef() override; @@ -186,6 +192,7 @@ private: sp<SurfaceControl> mSurfaceControl GUARDED_BY(mMutex); mutable std::mutex mMutex; + mutable std::mutex mWaitForBufferReleaseMutex; std::condition_variable mCallbackCV; // BufferQueue internally allows 1 more than @@ -324,6 +331,7 @@ private: std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex); + std::function<void()> mWaitForBufferReleaseCallback GUARDED_BY(mWaitForBufferReleaseMutex); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) // BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to the // client. diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h index 0948c4d076..f1c75d3a45 100644 --- a/libs/gui/include/gui/BufferQueue.h +++ b/libs/gui/include/gui/BufferQueue.h @@ -76,6 +76,9 @@ public: void onSetFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) override; #endif +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + void onSlotCountChanged(int slotCount) override; +#endif private: // mConsumerListener is a weak reference to the IConsumerListener. This is // the raison d'etre of ProxyConsumerListener. diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h index 6aa801ab86..e00c44eb70 100644 --- a/libs/gui/include/gui/BufferQueueConsumer.h +++ b/libs/gui/include/gui/BufferQueueConsumer.h @@ -96,11 +96,26 @@ public: // This should be called from the onBuffersReleased() callback. virtual status_t getReleasedBuffers(uint64_t* outSlotMask); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // getReleasedBuffers sets the values pointed to by outSlotMask to the bits + // indicating which buffer slots have been released by the BufferQueue + // but have not yet been released by the consumer. + // + // This should be called from the onBuffersReleased() callback when + // allowUnlimitedSlots has been called. + virtual status_t getReleasedBuffersExtended(std::vector<bool>* outSlotMask) override; +#endif + // setDefaultBufferSize is used to set the size of buffers returned by // dequeueBuffer when a width and height of zero is requested. Default // is 1x1. virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // see IGraphicBufferConsumer::allowUnlimitedSlots + virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) override; +#endif + // see IGraphicBufferConsumer::setMaxBufferCount virtual status_t setMaxBufferCount(int bufferCount); diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h index 77cdf2c9f3..7f92a46053 100644 --- a/libs/gui/include/gui/BufferQueueCore.h +++ b/libs/gui/include/gui/BufferQueueCore.h @@ -32,10 +32,11 @@ #include <utils/Trace.h> #include <utils/Vector.h> +#include <condition_variable> #include <list> -#include <set> #include <mutex> -#include <condition_variable> +#include <set> +#include <vector> #define ATRACE_BUFFER_INDEX(index) \ do { \ @@ -91,6 +92,10 @@ private: // Dump our state in a string void dumpState(const String8& prefix, String8* outResult) const; + // getTotalSlotCountLocked returns the total number of slots in use by the + // buffer queue at this time. + int getTotalSlotCountLocked() const; + // getMinUndequeuedBufferCountLocked returns the minimum number of buffers // that must remain in a state other than DEQUEUED. The async parameter // tells whether we're in asynchronous mode. @@ -120,6 +125,10 @@ private: int getMaxBufferCountLocked(bool asyncMode, bool dequeueBufferCannotBlock, int maxBufferCount) const; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // This resizes mSlots to the given size, but only if it's increasing. + status_t extendSlotCountLocked(int size); +#endif // clearBufferSlotLocked frees the GraphicBuffer and sync resources for the // given slot. void clearBufferSlotLocked(int slot); @@ -204,7 +213,7 @@ private: // mConnectedProducerListener will not trigger onBufferAttached() callback. bool mBufferAttachedCbEnabled; - // mSlots is an array of buffer slots that must be mirrored on the producer + // mSlots is a collection of buffer slots that must be mirrored on the producer // side. This allows buffer ownership to be transferred between the producer // and consumer without sending a GraphicBuffer over Binder. The entire // array is initialized to NULL at construction time, and buffers are @@ -266,8 +275,14 @@ private: // is specified. android_dataspace mDefaultBufferDataSpace; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // mAllowExtendedSlotCount is set by the consumer to permit the producer to + // request an unlimited number of slots. + bool mAllowExtendedSlotCount; +#endif + // mMaxBufferCount is the limit on the number of buffers that will be - // allocated at one time. This limit can be set by the consumer. + // allocated at one time. int mMaxBufferCount; // mMaxAcquiredBufferCount is the number of buffers that the consumer may diff --git a/libs/gui/include/gui/BufferQueueDefs.h b/libs/gui/include/gui/BufferQueueDefs.h index ffafb49615..42cf439450 100644 --- a/libs/gui/include/gui/BufferQueueDefs.h +++ b/libs/gui/include/gui/BufferQueueDefs.h @@ -17,6 +17,7 @@ #ifndef ANDROID_GUI_BUFFERQUEUECOREDEFS_H #define ANDROID_GUI_BUFFERQUEUECOREDEFS_H +#include <com_android_graphics_libgui_flags.h> #include <gui/BufferSlot.h> #include <ui/BufferQueueDefs.h> @@ -24,7 +25,11 @@ namespace android { class BufferQueueCore; namespace BufferQueueDefs { - typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS]; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + typedef std::vector<BufferSlot> SlotsType; +#else + typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS]; +#endif } // namespace BufferQueueDefs } // namespace android diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h index 086ce7ce56..50abadb922 100644 --- a/libs/gui/include/gui/BufferQueueProducer.h +++ b/libs/gui/include/gui/BufferQueueProducer.h @@ -47,6 +47,11 @@ public: // flags indicating that previously-returned buffers are no longer valid. virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // see IGraphicsBufferProducer::extendSlotCount + virtual status_t extendSlotCount(int size) override; +#endif + // see IGraphicsBufferProducer::setMaxDequeuedBufferCount virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers); diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h index e976aa48be..5cd19c1583 100644 --- a/libs/gui/include/gui/ConsumerBase.h +++ b/libs/gui/include/gui/ConsumerBase.h @@ -185,7 +185,9 @@ protected: virtual void onFrameDetached(const uint64_t bufferId) override; virtual void onBuffersReleased() override; virtual void onSidebandStreamChanged() override; - +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + virtual void onSlotCountChanged(int slotCount) override; +#endif virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer); virtual status_t detachBufferLocked(int slotIndex); @@ -284,7 +286,11 @@ protected: // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + std::vector<Slot> mSlots; +#else Slot mSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; +#endif // mAbandoned indicates that the BufferQueue will no longer be used to // consume images buffers pushed to it using the IGraphicBufferProducer diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h index 845bc54c71..446841bcd2 100644 --- a/libs/gui/include/gui/Flags.h +++ b/libs/gui/include/gui/Flags.h @@ -46,6 +46,7 @@ typedef android::sp<android::IGraphicBufferProducer> ParcelableSurfaceType; namespace flagtools { sp<SurfaceType> surfaceToSurfaceType(const sp<Surface>& surface); +ParcelableSurfaceType surfaceToParcelableSurfaceType(const sp<Surface>& surface); ParcelableSurfaceType toParcelableSurfaceType(const view::Surface& surface); sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface); bool isSurfaceTypeValid(const sp<SurfaceType>& surface); diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index 8a66dc0cf1..30cbfa2f9f 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -266,6 +266,9 @@ protected: virtual status_t acquireBufferLocked(BufferItem *item, nsecs_t presentWhen, uint64_t maxFrameNumber = 0) override; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + virtual void onSlotCountChanged(int slotCount) override; +#endif // releaseBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase. virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, @@ -496,8 +499,11 @@ private: // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + std::vector<EglSlot> mEglSlots; +#else EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; - +#endif // mCurrentTexture is the buffer slot index of the buffer that is currently // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, // indicating that no buffer slot is currently bound to the texture. Note, diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h index 51d3959de7..1695aae876 100644 --- a/libs/gui/include/gui/IConsumerListener.h +++ b/libs/gui/include/gui/IConsumerListener.h @@ -98,6 +98,16 @@ public: virtual void onSetFrameRate(float /*frameRate*/, int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) {} #endif + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // Notifies the consumer that IGraphicBufferProducer::extendSlotCount has + // been called and the total slot count has increased. + // + // This will only ever be called if + // IGraphicBufferConsumer::allowUnlimitedSlots has been called on the + // consumer. + virtual void onSlotCountChanged(int /* slotCount */) {} +#endif }; #ifndef NO_BINDER diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h index 18f5488173..56eb291335 100644 --- a/libs/gui/include/gui/IGraphicBufferConsumer.h +++ b/libs/gui/include/gui/IGraphicBufferConsumer.h @@ -16,6 +16,7 @@ #pragma once +#include <com_android_graphics_libgui_flags.h> #include <gui/OccupancyTracker.h> #include <binder/IInterface.h> @@ -35,6 +36,10 @@ class Fence; class GraphicBuffer; class IConsumerListener; class NativeHandle; + +/* + * See IGraphicBufferProducer for details on SLOT_COUNT. + */ #ifndef NO_BINDER class IGraphicBufferConsumer : public IInterface { public: @@ -92,7 +97,7 @@ public: // // Return of a value other than NO_ERROR means an error has occurred: // * BAD_VALUE - the given slot number is invalid, either because it is out of the range - // [0, NUM_BUFFER_SLOTS) or because the slot it refers to is not + // [0, SLOT_COUNT) or because the slot it refers to is not // currently acquired. virtual status_t detachBuffer(int slot) = 0; @@ -173,6 +178,19 @@ public: // * NO_INIT - the BufferQueue has been abandoned. virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // getReleasedBuffersExtended for each slot, sets slotMask[slot] to 1 if it + // corresponds to a released buffer slot. In particular, a released buffer + // is one that has been released by the BufferQueue but has not yet been + // released by the consumer. + // + // This should be called from the onBuffersReleased() callback. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the BufferQueue has been abandoned. + virtual status_t getReleasedBuffersExtended(std::vector<bool>* slotMask) = 0; +#endif + // setDefaultBufferSize is used to set the size of buffers returned by dequeueBuffer when a // width and height of zero is requested. Default is 1x1. // @@ -180,6 +198,26 @@ public: // * BAD_VALUE - either w or h was zero virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // allowUnlimitedSlots allows the producer to set the upper bound on slots. + // + // Must be called before the producer is connected. If the producer + // increases the slot count, an IConsumerListener::onSlotCountChanged + // update is sent. + // + // This can not be used with setMaxBufferCount. Calls after + // setMaxBufferCount will fail and calls to setMaxBufferCount after setting + // this to true will fail. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the BufferQueue has been abandoned + // * INVALID_OPERATION - one of the following errors has occurred: + // * Producer has been connected + // * setMaxBufferCount has been called and shrunk the + // BufferQueue. + virtual status_t allowUnlimitedSlots(bool allowUnlimitedSlots) = 0; +#endif + // setMaxBufferCount sets the maximum value for the number of buffers used in the BufferQueue // (the initial default is NUM_BUFFER_SLOTS). If a call to setMaxAcquiredBufferCount (by the // consumer), or a call to setAsyncMode or setMaxDequeuedBufferCount (by the producer), would diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index a42ddc466c..7accca6298 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -72,6 +72,14 @@ using HGraphicBufferProducerV2_0 = * dequeueBuffer() to get an empty buffer, fills it with data, then * calls queueBuffer() to make it available to the consumer. * + * BufferQueues have a size, which we'll refer to in other comments as + * SLOT_COUNT. Its default is 64 (NUM_BUFFER_SLOTS). It can be adjusted by + * the IGraphicBufferConsumer::setMaxBufferCount, or when + * IGraphicBufferConsumer::allowUnlimitedSlots is set to true, by + * IGraphicBufferProducer::extendSlotCount. The actual number of buffers in use + * is a function of various configurations, including whether we're in single + * buffer mode, the maximum dequeuable/aquirable buffers, and SLOT_COUNT. + * * This class was previously called ISurfaceTexture. */ #ifndef NO_BINDER @@ -106,7 +114,7 @@ public: // slot->buffer mapping so that it's not necessary to transfer a // GraphicBuffer for every dequeue operation. // - // The slot must be in the range of [0, NUM_BUFFER_SLOTS). + // The slot must be in the range of [0, SLOT_COUNT). // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned or the producer is not @@ -116,6 +124,30 @@ public: // * buffer specified by the slot is not dequeued virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + // extendSlotCount sets the maximum slot count (SLOT_COUNT) to the given + // size. This feature must be enabled by the consumer to function via + // IGraphicBufferConsumer::allowUnlimitedSlots. This must be called before + // the producer connects. + // + // After calling this, any slot can be returned in the [0, size) range. + // Callers are responsible for the allocation of the appropriate slots + // array for their own buffer cache. + // + // On success, the consumer is notified (so that it can increase its own + // slot cache). + // + // Return of a value other than NO_ERROR means that an error has occurred: + // * NO_INIT - the buffer queue has been abandoned + // * INVALID_OPERATION - one of the following conditions has occurred: + // * The producer is connected already + // * The consumer didn't call allowUnlimitedSlots + // * BAD_VALUE - The value is smaller than the previous max size + // (initialized to 64, then whatever the last call to this + // was) + virtual status_t extendSlotCount(int size); +#endif + // setMaxDequeuedBufferCount sets the maximum number of buffers that can be // dequeued by the producer at one time. If this method succeeds, any new // buffer slots will be both unallocated and owned by the BufferQueue object @@ -129,7 +161,7 @@ public: // will result in a BAD_VALUE error. // // The buffer count should be at least 1 (inclusive), but at most - // (NUM_BUFFER_SLOTS - the minimum undequeued buffer count) (exclusive). The + // (SLOT_COUNT - the minimum undequeued buffer count) (exclusive). The // minimum undequeued buffer count can be obtained by calling // query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS). // @@ -239,8 +271,8 @@ public: // * NO_INIT - the buffer queue has been abandoned or the producer is not // connected. // * BAD_VALUE - the given slot number is invalid, either because it is - // out of the range [0, NUM_BUFFER_SLOTS), or because the slot - // it refers to is not currently dequeued and requested. + // out of the range [0, SLOT_COUNT), or because the slot it + // refers to is not currently dequeued and requested. virtual status_t detachBuffer(int slot) = 0; // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer, @@ -415,6 +447,7 @@ public: FrameEventHistoryDelta frameTimestamps; bool bufferReplaced{false}; int maxBufferCount{BufferQueueDefs::NUM_BUFFER_SLOTS}; + bool isSlotExpansionAllowed{false}; status_t result{NO_ERROR}; }; @@ -430,7 +463,7 @@ public: // below). Any other properties (zero point, etc) // are client-dependent, and should be documented by the client. // - // The slot must be in the range of [0, NUM_BUFFER_SLOTS). + // The slot must be in the range of [0, SLOT_COUNT). // // Upon success, the output will be filled with meaningful values // (refer to the documentation below). @@ -460,7 +493,7 @@ public: // // The buffer is not queued for use by the consumer. // - // The slot must be in the range of [0, NUM_BUFFER_SLOTS). + // The slot must be in the range of [0, SLOT_COUNT). // // The buffer will not be overwritten until the fence signals. The fence // will usually be the one obtained from dequeueBuffer. diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 1c31e46cb4..64f191b867 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -425,7 +425,7 @@ struct layer_state_t { PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE}; // A value indicating the significance of the layer's content to the app's desired user - // experience. A lower priority will result in more likelihood of getting access to limited + // experience. A higher value will result in more likelihood of getting access to limited // resources, such as picture processing hardware. int32_t appContentPriority = 0; diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 14a351316d..755674d9e6 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -558,7 +558,11 @@ protected: // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + std::vector<BufferSlot> mSlots; +#else BufferSlot mSlots[NUM_BUFFER_SLOTS]; +#endif // mReqWidth is the buffer width that will be requested at the next dequeue // operation. It is initialized to 1. @@ -732,6 +736,10 @@ protected: std::vector<sp<GraphicBuffer>> mRemovedBuffers; int mMaxBufferCount; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + bool mIsSlotExpansionAllowed; +#endif + sp<IProducerListener> mListenerProxy; // Get and flush the buffers of given slots, if the buffer in the slot diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0ce0c0a9c3..0f66c8b492 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -298,7 +298,9 @@ public: static status_t removeHdrLayerInfoListener(const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener); - static status_t setActivePictureListener(const sp<gui::IActivePictureListener>& listener); + static status_t addActivePictureListener(const sp<gui::IActivePictureListener>& listener); + + static status_t removeActivePictureListener(const sp<gui::IActivePictureListener>& listener); /* * Sends a power boost to the composer. This function is asynchronous. diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index f07747f32f..87051a7aac 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -55,6 +55,7 @@ cc_test { "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true", "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true", "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true", + "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_UNLIMITED_SLOTS=true", ], srcs: [ diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp index 3b6a66efe9..6453885804 100644 --- a/libs/gui/tests/BufferItemConsumer_test.cpp +++ b/libs/gui/tests/BufferItemConsumer_test.cpp @@ -22,8 +22,11 @@ #include <gui/BufferItemConsumer.h> #include <gui/IProducerListener.h> #include <gui/Surface.h> +#include <ui/BufferQueueDefs.h> #include <ui/GraphicBuffer.h> +#include <unordered_set> + namespace android { static constexpr int kWidth = 100; @@ -57,6 +60,8 @@ class BufferItemConsumerTest : public ::testing::Test { }; void SetUp() override { + mBuffers.resize(BufferQueueDefs::NUM_BUFFER_SLOTS); + mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true); String8 name("BufferItemConsumer_Under_Test"); mBIC->setName(name); @@ -137,6 +142,11 @@ class BufferItemConsumerTest : public ::testing::Test { ASSERT_EQ(NO_ERROR, ret); } + void DetachBuffer(int slot) { + ALOGD("detachBuffer: slot=%d", slot); + status_t ret = mBIC->detachBuffer(mBuffers[slot]); + ASSERT_EQ(NO_ERROR, ret); + } std::mutex mMutex; int mFreedBufferCount{0}; @@ -146,7 +156,7 @@ class BufferItemConsumerTest : public ::testing::Test { sp<BufferFreedListener> mBFL; sp<IGraphicBufferProducer> mProducer; sp<IGraphicBufferConsumer> mConsumer; - sp<GraphicBuffer> mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; + std::vector<sp<GraphicBuffer>> mBuffers; }; // Test that detaching buffer from consumer side triggers onBufferFreed. @@ -239,4 +249,52 @@ TEST_F(BufferItemConsumerTest, DetachBufferWithBuffer) { } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +TEST_F(BufferItemConsumerTest, UnlimitedSlots_AcquireReleaseAll) { + ASSERT_EQ(OK, mProducer->extendSlotCount(256)); + mBuffers.resize(256); + + ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(100)); + + std::unordered_set<int> slots; + for (int i = 0; i < 100; i++) { + int slot; + DequeueBuffer(&slot); + slots.insert(slot); + } + EXPECT_EQ(100u, slots.size()); + + for (int dequeuedSlot : slots) { + QueueBuffer(dequeuedSlot); + + int slot; + AcquireBuffer(&slot); + ReleaseBuffer(slot); + } +} + +TEST_F(BufferItemConsumerTest, UnlimitedSlots_AcquireDetachAll) { + ASSERT_EQ(OK, mProducer->extendSlotCount(256)); + mBuffers.resize(256); + + ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(100)); + + std::unordered_set<int> slots; + for (int i = 0; i < 100; i++) { + int slot; + DequeueBuffer(&slot); + slots.insert(slot); + } + EXPECT_EQ(100u, slots.size()); + + for (int dequeuedSlot : slots) { + QueueBuffer(dequeuedSlot); + + int slot; + AcquireBuffer(&slot); + DetachBuffer(slot); + } +} +#endif + } // namespace android diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 16060990bd..77b4ae8623 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -20,6 +20,8 @@ #include "Constants.h" #include "MockConsumer.h" +#include <EGL/egl.h> + #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> #include <gui/BufferQueue.h> @@ -44,7 +46,9 @@ #include <gtest/gtest.h> #include <future> +#include <optional> #include <thread> +#include <unordered_map> #include <com_android_graphics_libgui_flags.h> @@ -1612,4 +1616,221 @@ TEST_F(BufferQueueTest, PassesThroughPictureProfileHandle) { } } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +struct MockUnlimitedSlotConsumer : public MockConsumer { + virtual void onSlotCountChanged(int size) override { mSize = size; } + + std::optional<int> mSize; +}; + +TEST_F(BufferQueueTest, UnlimitedSlots_FailsWhenNotAllowed) { + createBufferQueue(); + + sp<MockUnlimitedSlotConsumer> mc = sp<MockUnlimitedSlotConsumer>::make(); + EXPECT_EQ(OK, mConsumer->consumerConnect(mc, false)); + + EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(64)); + EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(32)); + EXPECT_EQ(INVALID_OPERATION, mProducer->extendSlotCount(128)); + + EXPECT_EQ(std::nullopt, mc->mSize); +} + +TEST_F(BufferQueueTest, UnlimitedSlots_OnlyAllowedForExtensions) { + createBufferQueue(); + + sp<MockUnlimitedSlotConsumer> consumerListener = sp<MockUnlimitedSlotConsumer>::make(); + EXPECT_EQ(OK, mConsumer->consumerConnect(consumerListener, false)); + EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true)); + + EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(32)); + EXPECT_EQ(OK, mProducer->extendSlotCount(64)); + EXPECT_EQ(OK, mProducer->extendSlotCount(128)); + EXPECT_EQ(128, *consumerListener->mSize); + + EXPECT_EQ(OK, mProducer->extendSlotCount(128)); + EXPECT_EQ(BAD_VALUE, mProducer->extendSlotCount(127)); +} + +class BufferQueueUnlimitedTest : public BufferQueueTest { +protected: + static constexpr auto kMaxBufferCount = 128; + static constexpr auto kAcquirableBufferCount = 2; + static constexpr auto kDequeableBufferCount = kMaxBufferCount - kAcquirableBufferCount; + + virtual void SetUp() override { + BufferQueueTest::SetUp(); + + createBufferQueue(); + setUpConsumer(); + setUpProducer(); + } + + void setUpConsumer() { + EXPECT_EQ(OK, mConsumer->consumerConnect(mConsumerListener, false)); + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + EXPECT_EQ(OK, mConsumer->allowUnlimitedSlots(true)); +#endif + EXPECT_EQ(OK, mConsumer->setConsumerUsageBits(GraphicBuffer::USAGE_SW_READ_OFTEN)); + EXPECT_EQ(OK, mConsumer->setDefaultBufferSize(10, 10)); + EXPECT_EQ(OK, mConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888)); + EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(kAcquirableBufferCount)); + } + + void setUpProducer() { + EXPECT_EQ(OK, mProducer->extendSlotCount(kMaxBufferCount)); + + IGraphicBufferProducer::QueueBufferOutput output; + EXPECT_EQ(OK, + mProducer->connect(mProducerListener, NATIVE_WINDOW_API_CPU, + /*producerControlledByApp*/ true, &output)); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + ASSERT_TRUE(output.isSlotExpansionAllowed); +#endif + ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(kDequeableBufferCount)); + ASSERT_EQ(OK, mProducer->allowAllocation(true)); + } + + std::unordered_map<int, sp<Fence>> dequeueAll() { + std::unordered_map<int, sp<Fence>> slotsToFences; + + for (int i = 0; i < kDequeableBufferCount; ++i) { + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buffer; + + status_t ret = + mProducer->dequeueBuffer(&slot, &fence, /*w*/ 0, /*h*/ 0, /*format*/ 0, + /*uint64_t*/ 0, + /*outBufferAge*/ nullptr, /*outTimestamps*/ nullptr); + if (ret & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { + EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer)) + << "Unable to request buffer for slot " << slot; + } + EXPECT_FALSE(slotsToFences.contains(slot)); + slotsToFences.emplace(slot, fence); + } + EXPECT_EQ(kDequeableBufferCount, (int)slotsToFences.size()); + return slotsToFences; + } + + sp<MockUnlimitedSlotConsumer> mConsumerListener = sp<MockUnlimitedSlotConsumer>::make(); + sp<StubProducerListener> mProducerListener = sp<StubProducerListener>::make(); +}; + +TEST_F(BufferQueueUnlimitedTest, ExpandOverridesConsumerMaxBuffers) { + createBufferQueue(); + setUpConsumer(); + EXPECT_EQ(OK, mConsumer->setMaxBufferCount(10)); + + setUpProducer(); + + EXPECT_EQ(kDequeableBufferCount, (int)dequeueAll().size()); +} + +TEST_F(BufferQueueUnlimitedTest, CanDetachAll) { + auto slotsToFences = dequeueAll(); + for (auto& [slot, fence] : slotsToFences) { + EXPECT_EQ(OK, mProducer->detachBuffer(slot)); + } +} + +TEST_F(BufferQueueUnlimitedTest, CanCancelAll) { + auto slotsToFences = dequeueAll(); + for (auto& [slot, fence] : slotsToFences) { + EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence)); + } +} + +TEST_F(BufferQueueUnlimitedTest, CanAcquireAndReleaseAll) { + auto slotsToFences = dequeueAll(); + for (auto& [slot, fence] : slotsToFences) { + IGraphicBufferProducer::QueueBufferInput input; + input.fence = fence; + + IGraphicBufferProducer::QueueBufferOutput output; + EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + + BufferItem buffer; + EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0)); + EXPECT_EQ(OK, + mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY, + EGL_NO_SYNC, buffer.mFence)); + } +} + +TEST_F(BufferQueueUnlimitedTest, CanAcquireAndDetachAll) { + auto slotsToFences = dequeueAll(); + for (auto& [slot, fence] : slotsToFences) { + IGraphicBufferProducer::QueueBufferInput input; + input.fence = fence; + + IGraphicBufferProducer::QueueBufferOutput output; + EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + + BufferItem buffer; + EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0)); + EXPECT_EQ(OK, mConsumer->detachBuffer(buffer.mSlot)); + } +} + +TEST_F(BufferQueueUnlimitedTest, GetReleasedBuffersExtended) { + // First, acquire and release all the buffers so the consumer "knows" about + // them + auto slotsToFences = dequeueAll(); + + std::vector<bool> releasedSlots; + EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots)); + for (auto& [slot, _] : slotsToFences) { + EXPECT_TRUE(releasedSlots[slot]) + << "Slots that haven't been acquired will show up as released."; + } + for (auto& [slot, fence] : slotsToFences) { + IGraphicBufferProducer::QueueBufferInput input; + input.fence = fence; + + IGraphicBufferProducer::QueueBufferOutput output; + EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); + + BufferItem buffer; + EXPECT_EQ(OK, mConsumer->acquireBuffer(&buffer, 0)); + EXPECT_EQ(OK, + mConsumer->releaseBuffer(buffer.mSlot, buffer.mFrameNumber, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR, buffer.mFence)); + } + + EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots)); + for (auto& [slot, _] : slotsToFences) { + EXPECT_FALSE(releasedSlots[slot]) + << "Slots that have been acquired will show up as not released."; + } + + // Then, alternatively cancel and detach (release) buffers. Only detached + // buffers should be returned by getReleasedBuffersExtended + slotsToFences = dequeueAll(); + std::set<int> cancelledSlots; + std::set<int> detachedSlots; + bool cancel; + for (auto& [slot, fence] : slotsToFences) { + if (cancel) { + EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence)); + cancelledSlots.insert(slot); + } else { + EXPECT_EQ(OK, mProducer->detachBuffer(slot)); + detachedSlots.insert(slot); + } + cancel = !cancel; + } + + EXPECT_EQ(OK, mConsumer->getReleasedBuffersExtended(&releasedSlots)); + for (int slot : detachedSlots) { + EXPECT_TRUE(releasedSlots[slot]) << "Slots that are detached are released."; + } + for (int slot : cancelledSlots) { + EXPECT_FALSE(releasedSlots[slot]) + << "Slots that are still held in the queue are not released."; + } +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) } // namespace android diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp index f4239cb69e..9476930de3 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -803,6 +803,27 @@ INSTANTIATE_TEST_CASE_P(Rgba8888Tests, ::testing::ValuesIn(rgba8888TestSets)); #endif - - +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +TEST(CpuConsumerSlotTest, UnlimitedSlots_AcquireReleaseAll) { + sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(3); + sp<Surface> surface = cpuConsumer->getSurface(); + sp<SurfaceListener> listener = sp<StubSurfaceListener>::make(); + + ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener)); + ASSERT_EQ(OK, surface->setMaxDequeuedBufferCount(256)); + + std::vector<Surface::BatchBuffer> buffers(256); + EXPECT_EQ(OK, surface->dequeueBuffers(&buffers)); + + for (auto& buffer : buffers) { + sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(buffer.buffer); + sp<Fence> fence = sp<Fence>::make(buffer.fenceFd); + EXPECT_EQ(OK, surface->queueBuffer(graphicBuffer, fence)); + + CpuConsumer::LockedBuffer nativeBuffer; + EXPECT_EQ(OK, cpuConsumer->lockNextBuffer(&nativeBuffer)); + EXPECT_EQ(OK, cpuConsumer->unlockBuffer(nativeBuffer)); + } +} +#endif } // namespace android diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp index b60995a624..11383d906b 100644 --- a/libs/gui/tests/FillBuffer.cpp +++ b/libs/gui/tests/FillBuffer.cpp @@ -76,7 +76,7 @@ void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, } void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { - const size_t PIXEL_SIZE = 4; + constexpr size_t PIXEL_SIZE = 4; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { off_t offset = (y * stride + x) * PIXEL_SIZE; @@ -89,6 +89,21 @@ void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { } } +void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride, uint8_t r, uint8_t g, uint8_t b, + uint8_t a) { + constexpr size_t PIXEL_SIZE = 4; + + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + off_t offset = (y * stride + x) * PIXEL_SIZE; + buf[offset] = r; + buf[offset + 1] = g; + buf[offset + 2] = b; + buf[offset + 3] = a; + } + } +} + void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) { android_native_buffer_t* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), diff --git a/libs/gui/tests/FillBuffer.h b/libs/gui/tests/FillBuffer.h index b584179318..f5d6b8bbda 100644 --- a/libs/gui/tests/FillBuffer.h +++ b/libs/gui/tests/FillBuffer.h @@ -30,6 +30,8 @@ void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, const android_native_rect_t& rect); void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride); +void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride, uint8_t r, uint8_t g, uint8_t b, + uint8_t a); // Produce a single RGBA8 frame by filling a buffer with a checkerboard pattern // using the CPU. This assumes that the ANativeWindow is already configured to diff --git a/libs/gui/tests/FrameRateUtilsTest.cpp b/libs/gui/tests/FrameRateUtilsTest.cpp index 9ffe91f460..c5025331e1 100644 --- a/libs/gui/tests/FrameRateUtilsTest.cpp +++ b/libs/gui/tests/FrameRateUtilsTest.cpp @@ -34,7 +34,7 @@ TEST(FrameRateUtilsTest, ValidateFrameRate) { ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, "")); EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); - EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE, + EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "")); // Privileged APIs. diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp index 449533aa57..b22b85332c 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "SurfaceTextureGL_test" //#define LOG_NDEBUG 0 +#include <gmock/gmock.h> + #include "SurfaceTextureGL.h" #include "DisconnectWaiter.h" @@ -735,4 +737,30 @@ TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) { ASSERT_NE(NO_ERROR, mST->updateTexImage()); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +TEST_F(SurfaceTextureGLTest, TestUnlimitedSlots) { + ASSERT_EQ(OK, mSTC->connect(NATIVE_WINDOW_API_CPU, sp<StubSurfaceListener>::make())); + ASSERT_EQ(OK, mSTC->setMaxDequeuedBufferCount(256)); + + std::vector<Surface::BatchBuffer> buffers(256); + ASSERT_EQ(OK, mSTC->dequeueBuffers(&buffers)); + ASSERT_EQ(256u, buffers.size()); + ASSERT_THAT(buffers, Each(Field(&Surface::BatchBuffer::buffer, ::testing::NotNull()))); + + for (size_t i = 0; i < buffers.size(); ++i) { + sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(buffers[i].buffer); + sp<Fence> fence = sp<Fence>::make(buffers[i].fenceFd); + + void* buf; + ASSERT_EQ(OK, graphicBuffer->lock(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, &buf)); + fillRGBA8Buffer((uint8_t*)buf, graphicBuffer->getWidth(), graphicBuffer->getHeight(), + graphicBuffer->getStride(), i, i, i, i); + graphicBuffer->unlock(); + + ASSERT_EQ(OK, mSTC->queueBuffer(graphicBuffer, fence)); + ASSERT_EQ(OK, mST->updateTexImage()); + checkPixel(0, 0, i, i, i, i); + } +} +#endif } // namespace android diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 76362ff272..646e30e5a8 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -#include "gui/view/Surface.h" -#include "Constants.h" -#include "MockConsumer.h" - #include <gtest/gtest.h> #include <SurfaceFlingerProperties.h> @@ -36,10 +32,13 @@ #include <gui/IConsumerListener.h> #include <gui/IGraphicBufferConsumer.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/IProducerListener.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/SyncScreenCaptureListener.h> +#include <gui/view/Surface.h> +#include <nativebase/nativebase.h> #include <private/gui/ComposerService.h> #include <private/gui/ComposerServiceAIDL.h> #include <sys/types.h> @@ -47,6 +46,7 @@ #include <ui/BufferQueueDefs.h> #include <ui/DisplayMode.h> #include <ui/GraphicBuffer.h> +#include <ui/PixelFormat.h> #include <ui/Rect.h> #include <utils/Errors.h> #include <utils/String8.h> @@ -55,9 +55,12 @@ #include <cstddef> #include <cstdint> #include <future> +#include <iterator> #include <limits> #include <thread> +#include "Constants.h" +#include "MockConsumer.h" #include "testserver/TestServerClient.h" namespace android { @@ -1016,7 +1019,11 @@ public: return binder::Status::ok(); } - binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>&) { + binder::Status addActivePictureListener(const sp<gui::IActivePictureListener>&) { + return binder::Status::ok(); + } + + binder::Status removeActivePictureListener(const sp<gui::IActivePictureListener>&) { return binder::Status::ok(); } @@ -2529,4 +2536,128 @@ TEST_F(SurfaceTest, QueueBufferOutput_TracksReplacements_Plural) { } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) +TEST_F(SurfaceTest, UnlimitedSlots_FailsOnIncompatibleConsumer) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make(); + + EXPECT_EQ(OK, consumer->allowUnlimitedSlots(false)); + EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true)); + + sp<Surface> surface = sp<Surface>::make(producer); + sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener)); + + EXPECT_NE(OK, surface->setMaxDequeuedBufferCount(128)) + << "We shouldn't be able to set high max buffer counts if the consumer doesn't allow " + "it"; +} + +TEST_F(SurfaceTest, UnlimitedSlots_CanDequeueAndQueueMoreThanOldMaximum) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make(); + + EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true)); + EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true)); + EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888)); + EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN)); + + sp<Surface> surface = sp<Surface>::make(producer); + sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener)); + + EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128)) + << "If unlimited slots are allowed, we should be able increase the max dequeued buffer " + "count arbitrarily"; + + std::vector<std::tuple<sp<GraphicBuffer>, sp<Fence>, int>> buffers; + for (int i = 0; i < 128; i++) { + sp<GraphicBuffer> buffer; + sp<Fence> fence; + ASSERT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)) << "Unable to dequeue buffer #" << i; + buffers.push_back({buffer, fence, i}); + } + + for (auto& [buffer, fence, idx] : buffers) { + ASSERT_EQ(OK, surface->queueBuffer(buffer, fence)) << "Unable to queue buffer #" << idx; + } +} + +TEST_F(SurfaceTest, UnlimitedSlots_CanDequeueAndDetachMoreThanOldMaximum) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make(); + + EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true)); + EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true)); + EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888)); + EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN)); + + sp<Surface> surface = sp<Surface>::make(producer); + sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener)); + + EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128)) + << "If unlimited slots are allowed, we should be able increase the max dequeued buffer " + "count arbitrarily"; + + std::vector<std::tuple<sp<GraphicBuffer>, sp<Fence>, int>> buffers; + for (int i = 0; i < 128; i++) { + sp<GraphicBuffer> buffer; + sp<Fence> fence; + ASSERT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)) << "Unable to dequeue buffer #" << i; + buffers.push_back({buffer, fence, i}); + } + + for (auto& [buffer, _, idx] : buffers) { + ASSERT_EQ(OK, surface->detachBuffer(buffer)) << "Unable to detach buffer #" << idx; + } +} + +TEST_F(SurfaceTest, UnlimitedSlots_BatchOperations) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<IConsumerListener> consumerListener = sp<FakeConsumer>::make(); + + EXPECT_EQ(OK, consumer->allowUnlimitedSlots(true)); + EXPECT_EQ(OK, consumer->consumerConnect(consumerListener, /* consumerListener */ true)); + EXPECT_EQ(OK, consumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888)); + EXPECT_EQ(OK, consumer->setConsumerUsageBits(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN)); + + sp<Surface> surface = sp<Surface>::make(producer); + sp<SurfaceListener> surfaceListener = sp<StubSurfaceListener>::make(); + EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener)); + + EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(128)) + << "If unlimited slots are allowed, we should be able increase the max dequeued buffer " + "count arbitrarily"; + + std::vector<Surface::BatchBuffer> buffers(128); + EXPECT_EQ(OK, surface->dequeueBuffers(&buffers)); + EXPECT_EQ(128u, buffers.size()); + + std::vector<Surface::BatchQueuedBuffer> queuedBuffers; + std::transform(buffers.begin(), buffers.end(), std::back_inserter(queuedBuffers), + [](Surface::BatchBuffer& buffer) { + Surface::BatchQueuedBuffer out; + out.buffer = buffer.buffer; + out.fenceFd = buffer.fenceFd; + return out; + }); + + std::vector<SurfaceQueueBufferOutput> outputs; + EXPECT_EQ(OK, surface->queueBuffers(queuedBuffers, &outputs)); + EXPECT_EQ(128u, outputs.size()); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) } // namespace android diff --git a/libs/gui/tests/benchmarks/Android.bp b/libs/gui/tests/benchmarks/Android.bp new file mode 100644 index 0000000000..a728bef977 --- /dev/null +++ b/libs/gui/tests/benchmarks/Android.bp @@ -0,0 +1,27 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_benchmark { + name: "libgui_benchmarks", + srcs: [ + "*.cpp", + ], + defaults: ["libgui-defaults"], + static_libs: [ + "libgmock", + "libgtest", + ], + shared_libs: [ + "libgui", + ], + header_libs: [ + "libsurfaceflinger_mocks_headers", + "surfaceflinger_tests_common_headers", + ], +} diff --git a/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp b/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp new file mode 100644 index 0000000000..0a5189538b --- /dev/null +++ b/libs/gui/tests/benchmarks/Transaction_benchmarks.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2024 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 <benchmark/benchmark.h> +#include <cstddef> +#include <optional> +#include <vector> +#include "binder/Parcel.h" +#include "gui/SurfaceComposerClient.h" +#include "gui/SurfaceControl.h" +#include "log/log_main.h" + +namespace android { +namespace { +using android::hardware::graphics::common::V1_1::BufferUsage; + +std::vector<sp<SurfaceControl>> createSurfaceControl(const char* name, size_t num) { + sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); + LOG_FATAL_IF(client->initCheck() != OK, "Could not init SurfaceComposerClient"); + std::vector<sp<SurfaceControl>> surfaceControls; + for (size_t i = 0; i < num; i++) { + surfaceControls.push_back( + client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState)); + } + return surfaceControls; +} + +void applyTransaction(benchmark::State& state) { + std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */); + for (auto _ : state) { + SurfaceComposerClient::Transaction t; + for (auto& sc : surfaceControls) { + t.setCrop(sc, FloatRect{1, 2, 3, 4}); + t.setAutoRefresh(sc, true); + t.hide(sc); + t.setAlpha(sc, 0.5); + t.setCornerRadius(sc, 0.8); + } + Parcel p; + t.writeToParcel(&p); + t.clear(); + benchmark::DoNotOptimize(t); + } +} +BENCHMARK(applyTransaction); + +// Mimic a buffer transaction with callbacks +void applyBufferTransaction(benchmark::State& state) { + std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */); + std::vector<sp<GraphicBuffer>> buffers; + for (size_t i = 0; i < surfaceControls.size(); i++) { + int64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; + buffers.emplace_back( + sp<GraphicBuffer>::make(5, 5, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test")); + } + + for (auto _ : state) { + SurfaceComposerClient::Transaction t; + int i = 0; + for (auto& sc : surfaceControls) { + std::function<void(const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/, + std::optional<uint32_t> currentMaxAcquiredBufferCount)> + releaseBufferCallback; + t.setBuffer(sc, buffers[i], std::nullopt, std::nullopt, 5, releaseBufferCallback); + } + Parcel p; + // proxy for applying the transaction + t.writeToParcel(&p); + t.clear(); + benchmark::DoNotOptimize(t); + } +} +BENCHMARK(applyBufferTransaction); + +void mergeTransaction(benchmark::State& state) { + std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */); + for (auto _ : state) { + SurfaceComposerClient::Transaction t1; + for (auto& sc : surfaceControls) { + t1.setCrop(sc, FloatRect{1, 2, 3, 4}); + t1.setAutoRefresh(sc, true); + t1.hide(sc); + t1.setAlpha(sc, 0.5); + t1.setCornerRadius(sc, 0.8); + } + + SurfaceComposerClient::Transaction t2; + for (auto& sc : surfaceControls) { + t2.hide(sc); + t2.setAlpha(sc, 0.5); + t2.setCornerRadius(sc, 0.8); + t2.setBackgroundBlurRadius(sc, 5); + } + t1.merge(std::move(t2)); + benchmark::DoNotOptimize(t1); + } +} +BENCHMARK(mergeTransaction); + +void readTransactionFromParcel(benchmark::State& state) { + std::vector<sp<SurfaceControl>> surfaceControls = createSurfaceControl(__func__, 5 /* num */); + SurfaceComposerClient::Transaction t; + for (auto& sc : surfaceControls) { + t.setCrop(sc, FloatRect{1, 2, 3, 4}); + t.setAutoRefresh(sc, true); + t.hide(sc); + t.setAlpha(sc, 0.5); + t.setCornerRadius(sc, 0.8); + } + Parcel p; + t.writeToParcel(&p); + t.clear(); + + for (auto _ : state) { + SurfaceComposerClient::Transaction t2; + t2.readFromParcel(&p); + p.setDataPosition(0); + benchmark::DoNotOptimize(t2); + } +} +BENCHMARK(readTransactionFromParcel); + +} // namespace +} // namespace android diff --git a/libs/gui/tests/benchmarks/main.cpp b/libs/gui/tests/benchmarks/main.cpp new file mode 100644 index 0000000000..685c7c6a26 --- /dev/null +++ b/libs/gui/tests/benchmarks/main.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2024 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 <benchmark/benchmark.h> +BENCHMARK_MAIN(); diff --git a/libs/input/Android.bp b/libs/input/Android.bp index a4ae54b351..389fb7f6ab 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -262,6 +262,7 @@ cc_library { shared_libs: [ "android.companion.virtualdevice.flags-aconfig-cc", + "libaconfig_storage_read_api_cc", "libbase", "libbinder", "libbinder_ndk", diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 90d29dd190..d2c49b113d 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -615,7 +615,7 @@ std::unique_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) ALOGE("%s: Null parcel", __func__); return nullptr; } - std::string loadFileName = parcel->readCString(); + std::string loadFileName = parcel->readString8().c_str(); std::unique_ptr<KeyCharacterMap> map = std::make_unique<KeyCharacterMap>(KeyCharacterMap(loadFileName)); map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32()); @@ -704,7 +704,7 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { ALOGE("%s: Null parcel", __func__); return; } - parcel->writeCString(mLoadFileName.c_str()); + parcel->writeString8(String8(mLoadFileName.c_str())); parcel->writeInt32(static_cast<int32_t>(mType)); parcel->writeBool(mLayoutOverlayApplied); diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index 31592cd6e3..6ce3fba477 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -76,6 +76,9 @@ interface IInputConstants /* The default pointer acceleration value. */ const int DEFAULT_POINTER_ACCELERATION = 3; + /* The default mouse wheel acceleration value. */ + const int DEFAULT_MOUSE_WHEEL_ACCELERATION = 4; + /** * Use the default Velocity Tracker Strategy. Different axes may use different default * strategies. diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index d1c564d020..0167c433cb 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -66,6 +66,7 @@ cc_test { }, }, shared_libs: [ + "libaconfig_storage_read_api_cc", "libbase", "libbinder", "libcutils", diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index ed3e8c1a62..10abb7c927 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -258,11 +258,11 @@ enum ANativeWindow_FrameRateCompatibility { ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1, /** - * The window requests a frame rate that is greater than or equal to the specified frame rate. + * The window requests a frame rate that is at least the specified frame rate. * This value should be used for UIs, animations, scrolling, and anything that is not a game * or video. */ - ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE = 2 + ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST = 2 }; /** diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h index 243532bc4d..7e179d6f7c 100644 --- a/libs/permission/include/binder/AppOpsManager.h +++ b/libs/permission/include/binder/AppOpsManager.h @@ -148,7 +148,10 @@ public: OP_BLUETOOTH_ADVERTISE = 114, OP_RECORD_INCOMING_PHONE_AUDIO = 115, OP_NEARBY_WIFI_DEVICES = 116, - _NUM_OP = 117 + // 116 - 154 omitted due to lack of use in native + OP_CONTROL_AUDIO = 154, + OP_CONTROL_AUDIO_PARTIAL = 155, + _NUM_OP = 156, }; enum { diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 907590a236..873fc67c65 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -107,16 +107,15 @@ ftl::Future<FenceResult> RenderEngine::drawLayers(const DisplaySettings& display return resultFuture; } -ftl::Future<FenceResult> RenderEngine::drawGainmap( - const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, +ftl::Future<FenceResult> RenderEngine::tonemapAndDrawGainmap( const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, - float hdrSdrRatio, ui::Dataspace dataspace, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, const std::shared_ptr<ExternalTexture>& gainmap) { const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); std::future<FenceResult> resultFuture = resultPromise->get_future(); updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()}); - drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr, - std::move(hdrFence), hdrSdrRatio, dataspace, gainmap); + tonemapAndDrawGainmapInternal(std::move(resultPromise), hdr, std::move(hdrFence), hdrSdrRatio, + dataspace, sdr, gainmap); return resultFuture; } diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 95c4d033e2..c2dd4ae97e 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -217,12 +217,17 @@ public: const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence); - virtual ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr, - base::borrowed_fd&& sdrFence, - const std::shared_ptr<ExternalTexture>& hdr, - base::borrowed_fd&& hdrFence, float hdrSdrRatio, - ui::Dataspace dataspace, - const std::shared_ptr<ExternalTexture>& gainmap); + // Tonemaps an HDR input image and draws an SDR rendition, plus a gainmap + // describing how to recover the HDR image. + // + // The HDR input image is ALWAYS encoded with an sRGB transfer function and + // is a floating point format. Accordingly, the hdrSdrRatio describes the + // max luminance in the HDR input image above SDR, and the dataspace + // describes the input primaries. + virtual ftl::Future<FenceResult> tonemapAndDrawGainmap( + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, + const std::shared_ptr<ExternalTexture>& gainmap); // Clean-up method that should be called on the main thread after the // drawFence returned by drawLayers fires. This method will free up @@ -310,11 +315,10 @@ protected: const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) = 0; - virtual void drawGainmapInternal( + virtual void tonemapAndDrawGainmapInternal( const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, - float hdrSdrRatio, ui::Dataspace dataspace, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, const std::shared_ptr<ExternalTexture>& gainmap) = 0; }; diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index fb8331d870..c42e4034e0 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -46,17 +46,16 @@ public: ftl::Future<FenceResult>(const DisplaySettings&, const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&, base::unique_fd&&)); - MOCK_METHOD7(drawGainmap, + MOCK_METHOD6(tonemapAndDrawGainmap, ftl::Future<FenceResult>(const std::shared_ptr<ExternalTexture>&, - base::borrowed_fd&&, - const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, float, ui::Dataspace, + const std::shared_ptr<ExternalTexture>&, const std::shared_ptr<ExternalTexture>&)); - MOCK_METHOD8(drawGainmapInternal, + MOCK_METHOD7(tonemapAndDrawGainmapInternal, void(const std::shared_ptr<std::promise<FenceResult>>&&, - const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, float, - ui::Dataspace, const std::shared_ptr<ExternalTexture>&)); + ui::Dataspace, const std::shared_ptr<ExternalTexture>&, + const std::shared_ptr<ExternalTexture>&)); MOCK_METHOD5(drawLayersInternal, void(const std::shared_ptr<std::promise<FenceResult>>&&, const DisplaySettings&, const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&, diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 7c9ea930d9..14d08eea74 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -576,9 +576,7 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( if (usingLocalTonemap) { const float inputRatio = hdrType == HdrRenderType::GENERIC_HDR ? 1.0f : parameters.layerDimmingRatio; - static MouriMap kMapper; - shader = kMapper.mouriMap(getActiveContext(), shader, inputRatio, - parameters.display.targetHdrSdrRatio); + shader = localTonemap(shader, inputRatio, parameters.display.targetHdrSdrRatio); } // disable tonemapping if we already locally tonemapped @@ -619,6 +617,12 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( return shader; } +sk_sp<SkShader> SkiaRenderEngine::localTonemap(sk_sp<SkShader> shader, float inputMultiplier, + float targetHdrSdrRatio) { + static MouriMap kMapper; + return kMapper.mouriMap(getActiveContext(), shader, inputMultiplier, targetHdrSdrRatio); +} + void SkiaRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) { if (CC_UNLIKELY(mCapture->isCaptureRunning())) { // Record display settings when capture is running. @@ -1221,44 +1225,58 @@ void SkiaRenderEngine::drawLayersInternal( resultPromise->set_value(std::move(drawFence)); } -void SkiaRenderEngine::drawGainmapInternal( +void SkiaRenderEngine::tonemapAndDrawGainmapInternal( const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, - float hdrSdrRatio, ui::Dataspace dataspace, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, const std::shared_ptr<ExternalTexture>& gainmap) { std::lock_guard<std::mutex> lock(mRenderingMutex); auto context = getActiveContext(); - auto surfaceTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true); - sk_sp<SkSurface> dstSurface = - surfaceTextureRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR); - - waitFence(context, sdrFence); - const auto sdrTextureRef = getOrCreateBackendTexture(sdr->getBuffer(), false); - const auto sdrImage = sdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType); - const auto sdrShader = - sdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, - SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}), - nullptr); + auto gainmapTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true); + sk_sp<SkSurface> gainmapSurface = + gainmapTextureRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR); + + auto sdrTextureRef = getOrCreateBackendTexture(sdr->getBuffer(), true); + sk_sp<SkSurface> sdrSurface = sdrTextureRef->getOrCreateSurface(dataspace); + waitFence(context, hdrFence); const auto hdrTextureRef = getOrCreateBackendTexture(hdr->getBuffer(), false); const auto hdrImage = hdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType); const auto hdrShader = hdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, - SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}), + SkSamplingOptions({SkFilterMode::kNearest, SkMipmapMode::kNone}), nullptr); + const auto tonemappedShader = localTonemap(hdrShader, 1.0f, 1.0f); + static GainmapFactory kGainmapFactory; - const auto gainmapShader = kGainmapFactory.createSkShader(sdrShader, hdrShader, hdrSdrRatio); + const auto gainmapShader = + kGainmapFactory.createSkShader(tonemappedShader, hdrShader, hdrSdrRatio); - const auto canvas = dstSurface->getCanvas(); - SkPaint paint; - paint.setShader(gainmapShader); - paint.setBlendMode(SkBlendMode::kSrc); - canvas->drawPaint(paint); + sp<Fence> drawFence; - auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface)); - trace(drawFence); + { + const auto canvas = sdrSurface->getCanvas(); + SkPaint paint; + paint.setShader(tonemappedShader); + paint.setBlendMode(SkBlendMode::kSrc); + canvas->drawPaint(paint); + + drawFence = sp<Fence>::make(flushAndSubmit(context, sdrSurface)); + trace(drawFence); + } + + { + const auto canvas = gainmapSurface->getCanvas(); + SkPaint paint; + paint.setShader(gainmapShader); + paint.setBlendMode(SkBlendMode::kSrc); + canvas->drawPaint(paint); + + auto gmFence = sp<Fence>::make(flushAndSubmit(context, gainmapSurface)); + trace(gmFence); + drawFence = Fence::merge("gm-ss", drawFence, gmFence); + } resultPromise->set_value(std::move(drawFence)); } diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index 7be4c253e7..92b7af985c 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -143,13 +143,11 @@ private: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override final; - void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const std::shared_ptr<ExternalTexture>& sdr, - base::borrowed_fd&& sdrFence, - const std::shared_ptr<ExternalTexture>& hdr, - base::borrowed_fd&& hdrFence, float hdrSdrRatio, - ui::Dataspace dataspace, - const std::shared_ptr<ExternalTexture>& gainmap) override final; + void tonemapAndDrawGainmapInternal( + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, + const std::shared_ptr<ExternalTexture>& gainmap) override final; void dump(std::string& result) override final; @@ -168,6 +166,8 @@ private: }; sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&); + sk_sp<SkShader> localTonemap(sk_sp<SkShader>, float inputMultiplier, float targetHdrSdrRatio); + const PixelFormat mDefaultPixelFormat; // Identifier used for various mappings of layers to various diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp index 5e9dfbba3e..10218bb822 100644 --- a/libs/renderengine/skia/filters/LutShader.cpp +++ b/libs/renderengine/skia/filters/LutShader.cpp @@ -39,10 +39,13 @@ static const SkString kShader = SkString(R"( uniform int key; uniform int dimension; uniform vec3 luminanceCoefficients; // for CIE_Y + // for hlg/pq transfer function, we need normalize it to [0.0, 1.0] + // we use `normalizeScalar` to do so + uniform float normalizeScalar; vec4 main(vec2 xy) { float4 rgba = image.eval(xy); - float3 linear = toLinearSrgb(rgba.rgb); + float3 linear = toLinearSrgb(rgba.rgb) * normalizeScalar; if (dimension == 1) { // RGB if (key == 0) { @@ -52,19 +55,19 @@ static const SkString kShader = SkString(R"( float gainR = lut.eval(vec2(indexR, 0.0) + 0.5).r; float gainG = lut.eval(vec2(indexG, 0.0) + 0.5).r; float gainB = lut.eval(vec2(indexB, 0.0) + 0.5).r; - return float4(linear.r * gainR, linear.g * gainG, linear.b * gainB, rgba.a); + linear = float3(linear.r * gainR, linear.g * gainG, linear.b * gainB); // MAX_RGB } else if (key == 1) { float maxRGB = max(linear.r, max(linear.g, linear.b)); float index = maxRGB * float(size - 1); float gain = lut.eval(vec2(index, 0.0) + 0.5).r; - return float4(linear * gain, rgba.a); + linear = linear * gain; // CIE_Y } else if (key == 2) { float y = dot(linear, luminanceCoefficients) / 3.0; float index = y * float(size - 1); float gain = lut.eval(vec2(index, 0.0) + 0.5).r; - return float4(linear * gain, rgba.a); + linear = linear * gain; } } else if (dimension == 3) { if (key == 0) { @@ -110,12 +113,10 @@ static const SkString kShader = SkString(R"( float3 c0 = mix(c00, c10, linear.g); float3 c1 = mix(c01, c11, linear.g); - float3 val = mix(c0, c1, linear.b); - - return float4(val, rgba.a); + linear = mix(c0, c1, linear.b); } } - return rgba; + return float4(linear, rgba.a); })"); // same as shader::toColorSpace function @@ -186,10 +187,16 @@ sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input, SkBitmap bitmap; bitmap.allocPixels(info); if (!bitmap.installPixels(info, buffer.data(), info.minRowBytes())) { - LOG_ALWAYS_FATAL("unable to install pixels"); + ALOGW("bitmap.installPixels failed, skip this Lut!"); + return input; } sk_sp<SkImage> lutImage = SkImages::RasterFromBitmap(bitmap); + if (!lutImage) { + ALOGW("Got a nullptr from SkImages::RasterFromBitmap, skip this Lut!"); + return input; + } + mBuilder->child("image") = input; mBuilder->child("lut") = lutImage->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp, @@ -197,9 +204,22 @@ sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input, ? SkSamplingOptions(SkFilterMode::kLinear) : SkSamplingOptions()); + float normalizeScalar = 1.0; + switch (srcDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_HLG: + normalizeScalar = 0.203; + break; + case HAL_DATASPACE_TRANSFER_ST2084: + normalizeScalar = 0.0203; + break; + default: + normalizeScalar = 1.0; + } const int uSize = static_cast<int>(size); const int uKey = static_cast<int>(samplingKey); const int uDimension = static_cast<int>(dimension); + const float uNormalizeScalar = static_cast<float>(normalizeScalar); + if (static_cast<LutProperties::SamplingKey>(samplingKey) == LutProperties::SamplingKey::CIE_Y) { // Use predefined colorspaces of input dataspace so that we can get D65 illuminant mat3 toXYZMatrix(toColorSpace(srcDataspace).getRGBtoXYZ()); @@ -211,6 +231,7 @@ sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input, mBuilder->uniform("size") = uSize; mBuilder->uniform("key") = uKey; mBuilder->uniform("dimension") = uDimension; + mBuilder->uniform("normalizeScalar") = uNormalizeScalar; return mBuilder->makeShader(); } diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp index b099bcf3d7..aa12cef615 100644 --- a/libs/renderengine/skia/filters/MouriMap.cpp +++ b/libs/renderengine/skia/filters/MouriMap.cpp @@ -30,12 +30,12 @@ sk_sp<SkRuntimeEffect> makeEffect(const SkString& sksl) { } const SkString kCrosstalkAndChunk16x16(R"( uniform shader bitmap; - uniform float hdrSdrRatio; + uniform float inputMultiplier; vec4 main(vec2 xy) { float maximum = 0.0; for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { - float3 linear = toLinearSrgb(bitmap.eval((xy - 0.5) * 16 + 0.5 + vec2(x, y)).rgb) * hdrSdrRatio; + float3 linear = toLinearSrgb(bitmap.eval((xy - 0.5) * 16 + 0.5 + vec2(x, y)).rgb) * inputMultiplier; float maxRGB = max(linear.r, max(linear.g, linear.b)); maximum = max(maximum, log2(max(maxRGB, 1.0))); } @@ -77,12 +77,12 @@ const SkString kTonemap(R"( uniform shader image; uniform shader lux; uniform float scaleFactor; - uniform float hdrSdrRatio; + uniform float inputMultiplier; uniform float targetHdrSdrRatio; vec4 main(vec2 xy) { float localMax = lux.eval(xy * scaleFactor).r; float4 rgba = image.eval(xy); - float3 linear = toLinearSrgb(rgba.rgb) * hdrSdrRatio; + float3 linear = toLinearSrgb(rgba.rgb) * inputMultiplier; if (localMax <= targetHdrSdrRatio) { return float4(fromLinearSrgb(linear), rgba.a); @@ -116,19 +116,19 @@ MouriMap::MouriMap() mTonemap(makeEffect(kTonemap)) {} sk_sp<SkShader> MouriMap::mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, - float hdrSdrRatio, float targetHdrSdrRatio) { - auto downchunked = downchunk(context, input, hdrSdrRatio); + float inputMultiplier, float targetHdrSdrRatio) { + auto downchunked = downchunk(context, input, inputMultiplier); auto localLux = blur(context, downchunked.get()); - return tonemap(input, localLux.get(), hdrSdrRatio, targetHdrSdrRatio); + return tonemap(input, localLux.get(), inputMultiplier, targetHdrSdrRatio); } sk_sp<SkImage> MouriMap::downchunk(SkiaGpuContext* context, sk_sp<SkShader> input, - float hdrSdrRatio) const { + float inputMultiplier) const { SkMatrix matrix; SkImage* image = input->isAImage(&matrix, (SkTileMode*)nullptr); SkRuntimeShaderBuilder crosstalkAndChunk16x16Builder(mCrosstalkAndChunk16x16); crosstalkAndChunk16x16Builder.child("bitmap") = input; - crosstalkAndChunk16x16Builder.uniform("hdrSdrRatio") = hdrSdrRatio; + crosstalkAndChunk16x16Builder.uniform("inputMultiplier") = inputMultiplier; // TODO: fp16 might be overkill. Most practical surfaces use 8-bit RGB for HDR UI and 10-bit YUV // for HDR video. These downsample operations compute log2(max(linear RGB, 1.0)). So we don't // care about LDR precision since they all resolve to LDR-max. For appropriately mastered HDR @@ -168,7 +168,7 @@ sk_sp<SkImage> MouriMap::blur(SkiaGpuContext* context, SkImage* input) const { LOG_ALWAYS_FATAL_IF(!blurSurface, "%s: Failed to create surface!", __func__); return makeImage(blurSurface.get(), blurBuilder); } -sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio, +sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, float inputMultiplier, float targetHdrSdrRatio) const { static constexpr float kScaleFactor = 1.0f / 128.0f; SkRuntimeShaderBuilder tonemapBuilder(mTonemap); @@ -177,7 +177,7 @@ sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, floa localLux->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone)); tonemapBuilder.uniform("scaleFactor") = kScaleFactor; - tonemapBuilder.uniform("hdrSdrRatio") = hdrSdrRatio; + tonemapBuilder.uniform("inputMultiplier") = inputMultiplier; tonemapBuilder.uniform("targetHdrSdrRatio") = targetHdrSdrRatio; return tonemapBuilder.makeShader(); } diff --git a/libs/renderengine/skia/filters/MouriMap.h b/libs/renderengine/skia/filters/MouriMap.h index 9ba2b6ff9d..f4bfa1549e 100644 --- a/libs/renderengine/skia/filters/MouriMap.h +++ b/libs/renderengine/skia/filters/MouriMap.h @@ -62,10 +62,13 @@ class MouriMap { public: MouriMap(); // Apply the MouriMap tonemmaping operator to the input. - // The HDR/SDR ratio describes the luminace range of the input. 1.0 means SDR. Anything larger - // then 1.0 means that there is headroom above the SDR region. - // Similarly, the target HDR/SDR ratio describes the luminance range of the output. - sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float inputHdrSdrRatio, + // The inputMultiplier informs how to interpret the luminance encoding of the input. + // For a fixed point input, this is necessary to interpret what "1.0" means for the input + // pixels, so that the luminance can be scaled appropriately, such that the operator can + // transform SDR values to be within 1.0. For a floating point input, "1.0" always means SDR, + // so the caller must pass 1.0. + // The target HDR/SDR ratio describes the luminance range of the output. + sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float inputMultiplier, float targetHdrSdrRatio); private: diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index c187f93089..ea5605d4bd 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -249,11 +249,10 @@ void RenderEngineThreaded::drawLayersInternal( return; } -void RenderEngineThreaded::drawGainmapInternal( +void RenderEngineThreaded::tonemapAndDrawGainmapInternal( const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, - float hdrSdrRatio, ui::Dataspace dataspace, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, const std::shared_ptr<ExternalTexture>& gainmap) { resultPromise->set_value(Fence::NO_FENCE); return; @@ -281,10 +280,9 @@ ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( return resultFuture; } -ftl::Future<FenceResult> RenderEngineThreaded::drawGainmap( - const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, +ftl::Future<FenceResult> RenderEngineThreaded::tonemapAndDrawGainmap( const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, - float hdrSdrRatio, ui::Dataspace dataspace, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, const std::shared_ptr<ExternalTexture>& gainmap) { SFTRACE_CALL(); const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); @@ -292,13 +290,14 @@ ftl::Future<FenceResult> RenderEngineThreaded::drawGainmap( { std::lock_guard lock(mThreadMutex); mNeedsPostRenderCleanup = true; - mFunctionCalls.push([resultPromise, sdr, sdrFence = std::move(sdrFence), hdr, - hdrFence = std::move(hdrFence), hdrSdrRatio, dataspace, + mFunctionCalls.push([resultPromise, hdr, hdrFence = std::move(hdrFence), hdrSdrRatio, + dataspace, sdr, gainmap](renderengine::RenderEngine& instance) mutable { - SFTRACE_NAME("REThreaded::drawGainmap"); - instance.updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()}); - instance.drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr, - std::move(hdrFence), hdrSdrRatio, dataspace, gainmap); + SFTRACE_NAME("REThreaded::tonemapAndDrawGainmap"); + instance.updateProtectedContext({}, {hdr.get(), sdr.get(), gainmap.get()}); + instance.tonemapAndDrawGainmapInternal(std::move(resultPromise), hdr, + std::move(hdrFence), hdrSdrRatio, dataspace, sdr, + gainmap); }); } mCondition.notify_one(); diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index cb6e924d81..8554b55030 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -55,12 +55,10 @@ public: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override; - ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr, - base::borrowed_fd&& sdrFence, - const std::shared_ptr<ExternalTexture>& hdr, - base::borrowed_fd&& hdrFence, float hdrSdrRatio, - ui::Dataspace dataspace, - const std::shared_ptr<ExternalTexture>& gainmap) override; + ftl::Future<FenceResult> tonemapAndDrawGainmap( + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, + const std::shared_ptr<ExternalTexture>& gainmap) override; int getContextPriority() override; bool supportsBackgroundBlur() override; @@ -77,13 +75,11 @@ protected: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override; - void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const std::shared_ptr<ExternalTexture>& sdr, - base::borrowed_fd&& sdrFence, - const std::shared_ptr<ExternalTexture>& hdr, - base::borrowed_fd&& hdrFence, float hdrSdrRatio, - ui::Dataspace dataspace, - const std::shared_ptr<ExternalTexture>& gainmap) override; + void tonemapAndDrawGainmapInternal( + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, const std::shared_ptr<ExternalTexture>& sdr, + const std::shared_ptr<ExternalTexture>& gainmap) override; private: void threadMain(CreateInstanceFactory factory); diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp index 9a2d4f7463..8d8884a847 100644 --- a/libs/tracing_perfetto/Android.bp +++ b/libs/tracing_perfetto/Android.bp @@ -37,13 +37,17 @@ cc_library_shared { srcs: [ "tracing_perfetto.cpp", "tracing_perfetto_internal.cpp", + "tracing_sdk.cpp", ], shared_libs: [ "libbase", "libcutils", "libperfetto_c", - "android.os.flags-aconfig-cc-host", + ], + + export_shared_lib_headers: [ + "libperfetto_c", ], host_supported: true, diff --git a/libs/tracing_perfetto/include/tracing_sdk.h b/libs/tracing_perfetto/include/tracing_sdk.h new file mode 100644 index 0000000000..4a6e849567 --- /dev/null +++ b/libs/tracing_perfetto/include/tracing_sdk.h @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2024 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 <android-base/logging.h> +#include <stdint.h> + +#include <optional> +#include <vector> + +#include "perfetto/public/producer.h" +#include "perfetto/public/te_category_macros.h" +#include "perfetto/public/te_macros.h" +#include "perfetto/public/track_event.h" + +/** + * The objects declared here are intended to be managed by Java. + * This means the Java Garbage Collector is responsible for freeing the + * underlying native resources. + * + * The static methods prefixed with `delete_` are special. They are designed to be + * invoked by Java through the `NativeAllocationRegistry` when the + * corresponding Java object becomes unreachable. These methods act as + * callbacks to ensure proper deallocation of native resources. + */ +namespace tracing_perfetto { +/** + * @brief Represents extra data associated with a trace event. + * This class manages a collection of PerfettoTeHlExtra pointers. + */ +class Extra; + +/** + * @brief Emits a trace event. + * @param type The type of the event. + * @param cat The category of the event. + * @param name The name of the event. + * @param arg_ptr Pointer to Extra data. + */ +void trace_event(int type, const PerfettoTeCategory* cat, const char* name, + Extra* extra); + +/** + * @brief Gets the process track UUID. + */ +uint64_t get_process_track_uuid(); + +/** + * @brief Gets the thread track UUID for a given PID. + */ +uint64_t get_thread_track_uuid(pid_t tid); + +/** + * @brief Holder for all the other classes in the file. + */ +class Extra { + public: + Extra(); + void push_extra(PerfettoTeHlExtra* extra); + void pop_extra(); + void clear_extras(); + static void delete_extra(Extra* extra); + + PerfettoTeHlExtra* const* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(Extra); + + // These PerfettoTeHlExtra pointers are really pointers to all the other + // types of extras: Category, DebugArg, Counter etc. Those objects are + // individually managed by Java. + std::vector<PerfettoTeHlExtra*> extras_; +}; + +/** + * @brief Represents a trace event category. + */ +class Category { + public: + Category(const std::string& name, const std::string& tag, + const std::string& severity); + + ~Category(); + + void register_category(); + + void unregister_category(); + + bool is_category_enabled(); + + static void delete_category(Category* category); + + const PerfettoTeCategory* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(Category); + PerfettoTeCategory category_; + const std::string name_; + const std::string tag_; + const std::string severity_; +}; + +/** + * @brief Represents one end of a flow between two events. + */ +class Flow { + public: + Flow(); + + void set_process_flow(uint64_t id); + void set_process_terminating_flow(uint64_t id); + static void delete_flow(Flow* flow); + + const PerfettoTeHlExtraFlow* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(Flow); + PerfettoTeHlExtraFlow flow_; +}; + +/** + * @brief Represents a named track. + */ +class NamedTrack { + public: + NamedTrack(uint64_t id, uint64_t parent_uuid, const std::string& name); + + static void delete_track(NamedTrack* track); + + const PerfettoTeHlExtraNamedTrack* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(NamedTrack); + const std::string name_; + PerfettoTeHlExtraNamedTrack track_; +}; + +/** + * @brief Represents a registered track. + */ +class RegisteredTrack { + public: + RegisteredTrack(uint64_t id, uint64_t parent_uuid, const std::string& name, + bool is_counter); + ~RegisteredTrack(); + + void register_track(); + void unregister_track(); + static void delete_track(RegisteredTrack* track); + + const PerfettoTeHlExtraRegisteredTrack* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(RegisteredTrack); + PerfettoTeRegisteredTrack registered_track_; + PerfettoTeHlExtraRegisteredTrack track_; + const std::string name_; + const uint64_t id_; + const uint64_t parent_uuid_; + const bool is_counter_; +}; + +/** + * @brief Represents a counter track event. + * @tparam T The data type of the counter (int64_t or double). + */ +template <typename T> +class Counter { + public: + template <typename> + struct always_false : std::false_type {}; + + struct TypeMap { + using type = std::invoke_result_t<decltype([]() { + if constexpr (std::is_same_v<T, int64_t>) { + return std::type_identity<PerfettoTeHlExtraCounterInt64>{}; + } else if constexpr (std::is_same_v<T, double>) { + return std::type_identity<PerfettoTeHlExtraCounterDouble>{}; + } else { + return std::type_identity<void>{}; + } + })>::type; + + static constexpr int enum_value = []() { + if constexpr (std::is_same_v<T, int64_t>) { + return PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_INT64; + } else if constexpr (std::is_same_v<T, double>) { + return PERFETTO_TE_HL_EXTRA_TYPE_COUNTER_DOUBLE; + } else { + static_assert(always_false<T>::value, "Unsupported type"); + return 0; // Never reached, just to satisfy return type + } + }(); + }; + + Counter() { + static_assert(!std::is_same_v<typename TypeMap::type, void>, + "Unsupported type for Counter"); + + typename TypeMap::type counter; + counter.header = {TypeMap::enum_value}; + counter_ = std::move(counter); + } + + void set_value(T value) { + if constexpr (std::is_same_v<T, int64_t>) { + counter_.value = value; + } else if constexpr (std::is_same_v<T, double>) { + counter_.value = value; + } + } + + static void delete_counter(Counter* counter) { + delete counter; + } + + const TypeMap::type* get() const { + return &counter_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(Counter); + TypeMap::type counter_; +}; + +/** + * @brief Represents a debug argument for a trace event. + * @tparam T The data type of the argument (bool, int64_t, double, const char*). + */ +template <typename T> +class DebugArg { + public: + template <typename> + struct always_false : std::false_type {}; + + struct TypeMap { + using type = std::invoke_result_t<decltype([]() { + if constexpr (std::is_same_v<T, bool>) { + return std::type_identity<PerfettoTeHlExtraDebugArgBool>{}; + } else if constexpr (std::is_same_v<T, int64_t>) { + return std::type_identity<PerfettoTeHlExtraDebugArgInt64>{}; + } else if constexpr (std::is_same_v<T, double>) { + return std::type_identity<PerfettoTeHlExtraDebugArgDouble>{}; + } else if constexpr (std::is_same_v<T, const char*>) { + return std::type_identity<PerfettoTeHlExtraDebugArgString>{}; + } else { + return std::type_identity<void>{}; + } + })>::type; + + static constexpr int enum_value = []() { + if constexpr (std::is_same_v<T, bool>) { + return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_BOOL; + } else if constexpr (std::is_same_v<T, int64_t>) { + return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_INT64; + } else if constexpr (std::is_same_v<T, double>) { + return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_DOUBLE; + } else if constexpr (std::is_same_v<T, const char*>) { + return PERFETTO_TE_HL_EXTRA_TYPE_DEBUG_ARG_STRING; + } else { + static_assert(always_false<T>::value, "Unsupported type"); + return 0; // Never reached, just to satisfy return type + } + }(); + }; + + DebugArg(const std::string& name) : name_(name) { + static_assert(!std::is_same_v<typename TypeMap::type, void>, + "Unsupported type for DebugArg"); + + typename TypeMap::type arg; + arg.header = {TypeMap::enum_value}; + arg.name = name_.c_str(); + arg_ = std::move(arg); + } + + ~DebugArg() { + free_string_value(); + } + + void set_value(T value) { + if constexpr (std::is_same_v<T, const char*>) { + free_string_value(); + arg_.value = value; + } else if constexpr (std::is_same_v<T, int64_t>) { + arg_.value = value; + } else if constexpr (std::is_same_v<T, bool>) { + arg_.value = value; + } else if constexpr (std::is_same_v<T, double>) { + arg_.value = value; + } + } + + static void delete_arg(DebugArg* arg) { + delete arg; + } + + const TypeMap::type* get() const { + return &arg_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(DebugArg); + TypeMap::type arg_; + const std::string name_; + + constexpr void free_string_value() { + if constexpr (std::is_same_v<typename TypeMap::type, + PerfettoTeHlExtraDebugArgString>) { + if (arg_.value) { + free((void*)arg_.value); + arg_.value = nullptr; + } + } + } +}; + +template <typename T> +class ProtoField { + public: + template <typename> + struct always_false : std::false_type {}; + + struct TypeMap { + using type = std::invoke_result_t<decltype([]() { + if constexpr (std::is_same_v<T, int64_t>) { + return std::type_identity<PerfettoTeHlProtoFieldVarInt>{}; + } else if constexpr (std::is_same_v<T, double>) { + return std::type_identity<PerfettoTeHlProtoFieldDouble>{}; + } else if constexpr (std::is_same_v<T, const char*>) { + return std::type_identity<PerfettoTeHlProtoFieldCstr>{}; + } else { + return std::type_identity<void>{}; + } + })>::type; + + static constexpr PerfettoTeHlProtoFieldType enum_value = []() { + if constexpr (std::is_same_v<T, int64_t>) { + return PERFETTO_TE_HL_PROTO_TYPE_VARINT; + } else if constexpr (std::is_same_v<T, double>) { + return PERFETTO_TE_HL_PROTO_TYPE_DOUBLE; + } else if constexpr (std::is_same_v<T, const char*>) { + return PERFETTO_TE_HL_PROTO_TYPE_CSTR; + } else { + static_assert(always_false<T>::value, "Unsupported type"); + return 0; // Never reached, just to satisfy return type + } + }(); + }; + + ProtoField() { + static_assert(!std::is_same_v<typename TypeMap::type, void>, + "Unsupported type for ProtoField"); + + typename TypeMap::type arg; + arg.header.type = TypeMap::enum_value; + arg_ = std::move(arg); + } + + ~ProtoField() { + free_string_value(); + } + + void set_value(uint32_t id, T value) { + if constexpr (std::is_same_v<T, int64_t>) { + arg_.header.id = id; + arg_.value = value; + } else if constexpr (std::is_same_v<T, double>) { + arg_.header.id = id; + arg_.value = value; + } else if constexpr (std::is_same_v<T, const char*>) { + free_string_value(); + arg_.header.id = id; + arg_.str = value; + } + } + + static void delete_field(ProtoField* field) { + delete field; + } + + const TypeMap::type* get() const { + return &arg_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ProtoField); + TypeMap::type arg_; + + constexpr void free_string_value() { + if constexpr (std::is_same_v<typename TypeMap::type, + PerfettoTeHlProtoFieldCstr>) { + if (arg_.str) { + free((void*)arg_.str); + arg_.str = nullptr; + } + } + } +}; + +class ProtoFieldNested { + public: + ProtoFieldNested(); + + void add_field(PerfettoTeHlProtoField* field); + void set_id(uint32_t id); + static void delete_field(ProtoFieldNested* field); + + const PerfettoTeHlProtoFieldNested* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(ProtoFieldNested); + PerfettoTeHlProtoFieldNested field_; + // These PerfettoTeHlProtoField pointers are really pointers to all the other + // types of protos: PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldVarInt, + // PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldNested. Those objects are + // individually managed by Java. + std::vector<PerfettoTeHlProtoField*> fields_; +}; + +class Proto { + public: + Proto(); + + void add_field(PerfettoTeHlProtoField* field); + void clear_fields(); + static void delete_proto(Proto* proto); + + const PerfettoTeHlExtraProtoFields* get() const; + + private: + DISALLOW_COPY_AND_ASSIGN(Proto); + PerfettoTeHlExtraProtoFields proto_; + // These PerfettoTeHlProtoField pointers are really pointers to all the other + // types of protos: PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldVarInt, + // PerfettoTeHlProtoFieldVarInt, PerfettoTeHlProtoFieldNested. Those objects are + // individually managed by Java. + std::vector<PerfettoTeHlProtoField*> fields_; +}; + +/** + * @brief Activates a trigger. + * @param name The name of the trigger. + * @param ttl_ms The time-to-live of the trigger in milliseconds. + */ +void activate_trigger(const char* name, uint32_t ttl_ms); +} // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp index d203467783..0dab517b7f 100644 --- a/libs/tracing_perfetto/tests/Android.bp +++ b/libs/tracing_perfetto/tests/Android.bp @@ -21,12 +21,44 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +cc_library_static { + name: "libtracing_perfetto_test_utils", + export_include_dirs: [ + "include", + ], + static_libs: [ + "libflagtest", + "libgmock", + "perfetto_trace_protos", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wno-enum-compare", + "-Wno-unused-function", + ], + + srcs: [ + "utils.cpp", + ], + + shared_libs: [ + "libperfetto_c", + "liblog", + "libprotobuf-cpp-lite", + ], + export_shared_lib_headers: [ + "libperfetto_c", + ], +} + cc_test { name: "libtracing_perfetto_tests", static_libs: [ "libflagtest", "libgmock", "perfetto_trace_protos", + "libtracing_perfetto_test_utils", ], cflags: [ "-Wall", @@ -42,7 +74,6 @@ cc_test { ], srcs: [ "tracing_perfetto_test.cpp", - "utils.cpp", ], test_suites: ["device-tests"], } diff --git a/libs/tracing_perfetto/tests/include/utils.h b/libs/tracing_perfetto/tests/include/utils.h new file mode 100644 index 0000000000..b2630e1829 --- /dev/null +++ b/libs/tracing_perfetto/tests/include/utils.h @@ -0,0 +1,124 @@ +/* + * Copyright 2024 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. + */ + +// Copied from //external/perfetto/src/shared_lib/test/utils.h + +#ifndef UTILS_H +#define UTILS_H + +#include <cassert> +#include <condition_variable> +#include <cstdint> +#include <functional> +#include <iterator> +#include <memory> +#include <mutex> +#include <ostream> +#include <string> +#include <vector> + +#include "perfetto/public/abi/pb_decoder_abi.h" +#include "perfetto/public/pb_utils.h" +#include "perfetto/public/tracing_session.h" + +// Pretty printer for gtest +void PrintTo(const PerfettoPbDecoderField& field, std::ostream*); + +namespace perfetto { +namespace shlib { +namespace test_utils { + +class WaitableEvent { + public: + WaitableEvent() = default; + void Notify() { + std::unique_lock<std::mutex> lock(m_); + notified_ = true; + cv_.notify_one(); + } + bool WaitForNotification() { + std::unique_lock<std::mutex> lock(m_); + cv_.wait(lock, [this] { return notified_; }); + return notified_; + } + bool IsNotified() { + std::unique_lock<std::mutex> lock(m_); + return notified_; + } + + private: + std::mutex m_; + std::condition_variable cv_; + bool notified_ = false; +}; + +class TracingSession { + public: + class Builder { + public: + Builder() = default; + Builder& add_enabled_category(std::string category) { + enabled_categories_.push_back(std::move(category)); + return *this; + } + Builder& add_disabled_category(std::string category) { + disabled_categories_.push_back(std::move(category)); + return *this; + } + Builder& add_atrace_category(std::string category) { + atrace_categories_.push_back(std::move(category)); + return *this; + } + Builder& add_atrace_category_prefer_sdk(std::string category) { + atrace_categories_prefer_sdk_.push_back(std::move(category)); + return *this; + } + TracingSession Build(); + + private: + std::vector<std::string> enabled_categories_; + std::vector<std::string> disabled_categories_; + std::vector<std::string> atrace_categories_; + std::vector<std::string> atrace_categories_prefer_sdk_; + }; + + static TracingSession Adopt(struct PerfettoTracingSessionImpl*); + static TracingSession FromBytes(void *buf, size_t len); + + TracingSession(TracingSession&&) noexcept; + + ~TracingSession(); + + struct PerfettoTracingSessionImpl* session() const { + return session_; + } + + bool FlushBlocking(uint32_t timeout_ms); + void WaitForStopped(); + void StopBlocking(); + std::vector<uint8_t> ReadBlocking(); + + private: + TracingSession() = default; + struct PerfettoTracingSessionImpl* session_; + std::unique_ptr<WaitableEvent> stopped_; +}; + +} // namespace test_utils +} // namespace shlib +} // namespace perfetto + +#endif // UTILS_H diff --git a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp index e9fee2e6cf..b21a090677 100644 --- a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp +++ b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp @@ -22,6 +22,7 @@ #include <unistd.h> #include "gtest/gtest.h" + #include "perfetto/public/abi/data_source_abi.h" #include "perfetto/public/abi/heap_buffer.h" #include "perfetto/public/abi/pb_decoder_abi.h" diff --git a/libs/tracing_perfetto/tests/utils.cpp b/libs/tracing_perfetto/tests/utils.cpp index 8c4d4a8925..af61bc2192 100644 --- a/libs/tracing_perfetto/tests/utils.cpp +++ b/libs/tracing_perfetto/tests/utils.cpp @@ -34,36 +34,17 @@ namespace perfetto { namespace shlib { namespace test_utils { -namespace { - -std::string ToHexChars(uint8_t val) { - std::string ret; - uint8_t high_nibble = (val & 0xF0) >> 4; - uint8_t low_nibble = (val & 0xF); - static const char hex_chars[] = "0123456789ABCDEF"; - ret.push_back(hex_chars[high_nibble]); - ret.push_back(hex_chars[low_nibble]); - return ret; -} - -} // namespace - TracingSession TracingSession::Builder::Build() { perfetto::protos::TraceConfig trace_config; trace_config.add_buffers()->set_size_kb(1024); - auto* track_event_ds_config = trace_config.add_data_sources()->mutable_config(); - auto* ftrace_ds_config = trace_config.add_data_sources()->mutable_config(); - - track_event_ds_config->set_name("track_event"); - track_event_ds_config->set_target_buffer(0); - - ftrace_ds_config->set_name("linux.ftrace"); - ftrace_ds_config->set_target_buffer(0); - { - auto* ftrace_config = ftrace_ds_config->mutable_ftrace_config(); if (!atrace_categories_.empty()) { + auto* ftrace_ds_config = trace_config.add_data_sources()->mutable_config(); + ftrace_ds_config->set_name("linux.ftrace"); + ftrace_ds_config->set_target_buffer(0); + + auto* ftrace_config = ftrace_ds_config->mutable_ftrace_config(); ftrace_config->add_ftrace_events("ftrace/print"); for (const std::string& cat : atrace_categories_) { ftrace_config->add_atrace_categories(cat); @@ -76,8 +57,14 @@ TracingSession TracingSession::Builder::Build() { } { - auto* track_event_config = track_event_ds_config->mutable_track_event_config(); if (!enabled_categories_.empty() || !disabled_categories_.empty()) { + auto* track_event_ds_config = trace_config.add_data_sources()->mutable_config(); + + track_event_ds_config->set_name("track_event"); + track_event_ds_config->set_target_buffer(0); + + auto* track_event_config = track_event_ds_config->mutable_track_event_config(); + for (const std::string& cat : enabled_categories_) { track_event_config->add_enabled_categories(cat); } @@ -88,13 +75,17 @@ TracingSession TracingSession::Builder::Build() { } } - struct PerfettoTracingSessionImpl* ts = - PerfettoTracingSessionCreate(PERFETTO_BACKEND_SYSTEM); - std::string trace_config_string; trace_config.SerializeToString(&trace_config_string); - PerfettoTracingSessionSetup(ts, trace_config_string.data(), trace_config_string.length()); + return TracingSession::FromBytes(trace_config_string.data(), trace_config_string.length()); +} + +TracingSession TracingSession::FromBytes(void *buf, size_t len) { + struct PerfettoTracingSessionImpl* ts = + PerfettoTracingSessionCreate(PERFETTO_BACKEND_SYSTEM); + + PerfettoTracingSessionSetup(ts, buf, len); // Fails to start here PerfettoTracingSessionStartBlocking(ts); @@ -177,39 +168,3 @@ std::vector<uint8_t> TracingSession::ReadBlocking() { } // namespace test_utils } // namespace shlib } // namespace perfetto - -void PrintTo(const PerfettoPbDecoderField& field, std::ostream* pos) { - std::ostream& os = *pos; - PerfettoPbDecoderStatus status = - static_cast<PerfettoPbDecoderStatus>(field.status); - switch (status) { - case PERFETTO_PB_DECODER_ERROR: - os << "MALFORMED PROTOBUF"; - break; - case PERFETTO_PB_DECODER_DONE: - os << "DECODER DONE"; - break; - case PERFETTO_PB_DECODER_OK: - switch (field.wire_type) { - case PERFETTO_PB_WIRE_TYPE_DELIMITED: - os << "\""; - for (size_t i = 0; i < field.value.delimited.len; i++) { - os << perfetto::shlib::test_utils::ToHexChars( - field.value.delimited.start[i]) - << " "; - } - os << "\""; - break; - case PERFETTO_PB_WIRE_TYPE_VARINT: - os << "varint: " << field.value.integer64; - break; - case PERFETTO_PB_WIRE_TYPE_FIXED32: - os << "fixed32: " << field.value.integer32; - break; - case PERFETTO_PB_WIRE_TYPE_FIXED64: - os << "fixed64: " << field.value.integer64; - break; - } - break; - } -} diff --git a/libs/tracing_perfetto/tests/utils.h b/libs/tracing_perfetto/tests/utils.h deleted file mode 100644 index 8edb4143ee..0000000000 --- a/libs/tracing_perfetto/tests/utils.h +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Copyright 2024 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. - */ - -// Copied from //external/perfetto/src/shared_lib/test/utils.h - -#ifndef UTILS_H -#define UTILS_H - -#include <cassert> -#include <condition_variable> -#include <cstdint> -#include <functional> -#include <iterator> -#include <memory> -#include <mutex> -#include <ostream> -#include <string> -#include <vector> - -#include "gmock/gmock-matchers.h" -#include "gmock/gmock-more-matchers.h" -#include "gtest/gtest-matchers.h" -#include "gtest/gtest.h" -#include "perfetto/public/abi/pb_decoder_abi.h" -#include "perfetto/public/pb_utils.h" -#include "perfetto/public/tracing_session.h" - -// Pretty printer for gtest -void PrintTo(const PerfettoPbDecoderField& field, std::ostream*); - -namespace perfetto { -namespace shlib { -namespace test_utils { - -class WaitableEvent { - public: - WaitableEvent() = default; - void Notify() { - std::unique_lock<std::mutex> lock(m_); - notified_ = true; - cv_.notify_one(); - } - bool WaitForNotification() { - std::unique_lock<std::mutex> lock(m_); - cv_.wait(lock, [this] { return notified_; }); - return notified_; - } - bool IsNotified() { - std::unique_lock<std::mutex> lock(m_); - return notified_; - } - - private: - std::mutex m_; - std::condition_variable cv_; - bool notified_ = false; -}; - -class TracingSession { - public: - class Builder { - public: - Builder() = default; - Builder& add_enabled_category(std::string category) { - enabled_categories_.push_back(std::move(category)); - return *this; - } - Builder& add_disabled_category(std::string category) { - disabled_categories_.push_back(std::move(category)); - return *this; - } - Builder& add_atrace_category(std::string category) { - atrace_categories_.push_back(std::move(category)); - return *this; - } - Builder& add_atrace_category_prefer_sdk(std::string category) { - atrace_categories_prefer_sdk_.push_back(std::move(category)); - return *this; - } - TracingSession Build(); - - private: - std::vector<std::string> enabled_categories_; - std::vector<std::string> disabled_categories_; - std::vector<std::string> atrace_categories_; - std::vector<std::string> atrace_categories_prefer_sdk_; - }; - - static TracingSession Adopt(struct PerfettoTracingSessionImpl*); - - TracingSession(TracingSession&&) noexcept; - - ~TracingSession(); - - struct PerfettoTracingSessionImpl* session() const { - return session_; - } - - bool FlushBlocking(uint32_t timeout_ms); - void WaitForStopped(); - void StopBlocking(); - std::vector<uint8_t> ReadBlocking(); - - private: - TracingSession() = default; - struct PerfettoTracingSessionImpl* session_; - std::unique_ptr<WaitableEvent> stopped_; -}; - -template <typename FieldSkipper> -class FieldViewBase { - public: - class Iterator { - public: - using iterator_category = std::input_iterator_tag; - using value_type = const PerfettoPbDecoderField; - using pointer = value_type; - using reference = value_type; - reference operator*() const { - struct PerfettoPbDecoder decoder; - decoder.read_ptr = read_ptr_; - decoder.end_ptr = end_ptr_; - struct PerfettoPbDecoderField field; - do { - field = PerfettoPbDecoderParseField(&decoder); - } while (field.status == PERFETTO_PB_DECODER_OK && - skipper_.ShouldSkip(field)); - return field; - } - Iterator& operator++() { - struct PerfettoPbDecoder decoder; - decoder.read_ptr = read_ptr_; - decoder.end_ptr = end_ptr_; - PerfettoPbDecoderSkipField(&decoder); - read_ptr_ = decoder.read_ptr; - AdvanceToFirstInterestingField(); - return *this; - } - Iterator operator++(int) { - Iterator tmp = *this; - ++(*this); - return tmp; - } - - friend bool operator==(const Iterator& a, const Iterator& b) { - return a.read_ptr_ == b.read_ptr_; - } - friend bool operator!=(const Iterator& a, const Iterator& b) { - return a.read_ptr_ != b.read_ptr_; - } - - private: - Iterator(const uint8_t* read_ptr, const uint8_t* end_ptr, - const FieldSkipper& skipper) - : read_ptr_(read_ptr), end_ptr_(end_ptr), skipper_(skipper) { - AdvanceToFirstInterestingField(); - } - void AdvanceToFirstInterestingField() { - struct PerfettoPbDecoder decoder; - decoder.read_ptr = read_ptr_; - decoder.end_ptr = end_ptr_; - struct PerfettoPbDecoderField field; - const uint8_t* prev_read_ptr; - do { - prev_read_ptr = decoder.read_ptr; - field = PerfettoPbDecoderParseField(&decoder); - } while (field.status == PERFETTO_PB_DECODER_OK && - skipper_.ShouldSkip(field)); - if (field.status == PERFETTO_PB_DECODER_OK) { - read_ptr_ = prev_read_ptr; - } else { - read_ptr_ = decoder.read_ptr; - } - } - friend class FieldViewBase<FieldSkipper>; - const uint8_t* read_ptr_; - const uint8_t* end_ptr_; - const FieldSkipper& skipper_; - }; - using value_type = const PerfettoPbDecoderField; - using const_iterator = Iterator; - template <typename... Args> - explicit FieldViewBase(const uint8_t* begin, const uint8_t* end, Args... args) - : begin_(begin), end_(end), s_(args...) { - } - template <typename... Args> - explicit FieldViewBase(const std::vector<uint8_t>& data, Args... args) - : FieldViewBase(data.data(), data.data() + data.size(), args...) { - } - template <typename... Args> - explicit FieldViewBase(const struct PerfettoPbDecoderField& field, - Args... args) - : s_(args...) { - if (field.wire_type != PERFETTO_PB_WIRE_TYPE_DELIMITED) { - abort(); - } - begin_ = field.value.delimited.start; - end_ = begin_ + field.value.delimited.len; - } - Iterator begin() const { - return Iterator(begin_, end_, s_); - } - Iterator end() const { - return Iterator(end_, end_, s_); - } - PerfettoPbDecoderField front() const { - return *begin(); - } - - size_t size() const { - size_t count = 0; - for (auto field : *this) { - (void)field; - count++; - } - return count; - } - - bool ok() const { - for (auto field : *this) { - if (field.status != PERFETTO_PB_DECODER_OK) { - return false; - } - } - return true; - } - - private: - const uint8_t* begin_; - const uint8_t* end_; - FieldSkipper s_; -}; - -// Pretty printer for gtest -template <typename FieldSkipper> -void PrintTo(const FieldViewBase<FieldSkipper>& field_view, std::ostream* pos) { - std::ostream& os = *pos; - os << "{"; - for (PerfettoPbDecoderField f : field_view) { - PrintTo(f, pos); - os << ", "; - } - os << "}"; -} - -class IdFieldSkipper { - public: - explicit IdFieldSkipper(uint32_t id) : id_(id) { - } - explicit IdFieldSkipper(int32_t id) : id_(static_cast<uint32_t>(id)) { - } - bool ShouldSkip(const struct PerfettoPbDecoderField& field) const { - return field.id != id_; - } - - private: - uint32_t id_; -}; - -class NoFieldSkipper { - public: - NoFieldSkipper() = default; - bool ShouldSkip(const struct PerfettoPbDecoderField&) const { - return false; - } -}; - -// View over all the fields of a contiguous serialized protobuf message. -// -// Examples: -// -// for (struct PerfettoPbDecoderField field : FieldView(msg_begin, msg_end)) { -// //... -// } -// FieldView fields2(/*PerfettoPbDecoderField*/ nested_field); -// FieldView fields3(/*std::vector<uint8_t>*/ data); -// size_t num = fields1.size(); // The number of fields. -// bool ok = fields1.ok(); // Checks that the message is not malformed. -using FieldView = FieldViewBase<NoFieldSkipper>; - -// Like `FieldView`, but only considers fields with a specific id. -// -// Examples: -// -// IdFieldView fields(msg_begin, msg_end, id) -using IdFieldView = FieldViewBase<IdFieldSkipper>; - -// Matches a PerfettoPbDecoderField with the specified id. Accepts another -// matcher to match the contents of the field. -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, PbField(900, VarIntField(5))); -template <typename M> -auto PbField(int32_t id, M m) { - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::id, id), m); -} - -// Matches a PerfettoPbDecoderField submessage field. Accepts a container -// matcher for the subfields. -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, MsgField(ElementsAre(...))); -template <typename M> -auto MsgField(M m) { - auto f = [](const PerfettoPbDecoderField& field) { return FieldView(field); }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_DELIMITED), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField length delimited field. Accepts a string -// matcher. -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, StringField("string")); -template <typename M> -auto StringField(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return std::string( - reinterpret_cast<const char*>(field.value.delimited.start), - field.value.delimited.len); - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_DELIMITED), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField VarInt field. Accepts an integer matcher -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, VarIntField(1))); -template <typename M> -auto VarIntField(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return field.value.integer64; - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_VARINT), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField fixed64 field. Accepts an integer matcher -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, Fixed64Field(1))); -template <typename M> -auto Fixed64Field(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return field.value.integer64; - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_FIXED64), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField fixed32 field. Accepts an integer matcher -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, Fixed32Field(1))); -template <typename M> -auto Fixed32Field(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return field.value.integer32; - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_FIXED32), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField double field. Accepts a double matcher -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, DoubleField(1.0))); -template <typename M> -auto DoubleField(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return field.value.double_val; - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_FIXED64), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField float field. Accepts a float matcher -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, FloatField(1.0))); -template <typename M> -auto FloatField(M m) { - auto f = [](const PerfettoPbDecoderField& field) { - return field.value.float_val; - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_FIXED32), - testing::ResultOf(f, m)); -} - -// Matches a PerfettoPbDecoderField submessage field. Accepts a container -// matcher for the subfields. -// -// Example: -// PerfettoPbDecoderField field = ... -// EXPECT_THAT(field, AllFieldsWithId(900, ElementsAre(...))); -template <typename M> -auto AllFieldsWithId(int32_t id, M m) { - auto f = [id](const PerfettoPbDecoderField& field) { - return IdFieldView(field, id); - }; - return testing::AllOf( - testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), - testing::Field(&PerfettoPbDecoderField::wire_type, - PERFETTO_PB_WIRE_TYPE_DELIMITED), - testing::ResultOf(f, m)); -} - -} // namespace test_utils -} // namespace shlib -} // namespace perfetto - -#endif // UTILS_H diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp index c35e078b02..4b7021393f 100644 --- a/libs/tracing_perfetto/tracing_perfetto.cpp +++ b/libs/tracing_perfetto/tracing_perfetto.cpp @@ -17,6 +17,7 @@ #include "tracing_perfetto.h" #include <cutils/trace.h> + #include <cstdarg> #include "perfetto/public/te_category_macros.h" @@ -43,8 +44,10 @@ void traceBegin(uint64_t category, const char* name) { void traceFormatBegin(uint64_t category, const char* fmt, ...) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category); - const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory); + const bool preferAtrace = + internal::shouldPreferAtrace(perfettoTeCategory, category); + const bool preferPerfetto = + internal::isPerfettoCategoryEnabled(perfettoTeCategory); if (CC_LIKELY(!(preferAtrace || preferPerfetto))) { return; } @@ -57,7 +60,6 @@ void traceFormatBegin(uint64_t category, const char* fmt, ...) { vsnprintf(buf, BUFFER_SIZE, fmt, ap); va_end(ap); - if (preferAtrace) { atrace_begin(category, buf); } else if (preferPerfetto) { @@ -99,26 +101,28 @@ void traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) { } void traceAsyncBeginForTrack(uint64_t category, const char* name, - const char* trackName, int32_t cookie) { + const char* trackName, int32_t cookie) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_async_for_track_begin(category, trackName, name, cookie); } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { - internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie); + internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, + trackName, cookie); } } void traceAsyncEndForTrack(uint64_t category, const char* trackName, - int32_t cookie) { + int32_t cookie) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_async_for_track_end(category, trackName, cookie); } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { - internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie); + internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, + cookie); } } @@ -136,8 +140,10 @@ void traceInstant(uint64_t category, const char* name) { void traceFormatInstant(uint64_t category, const char* fmt, ...) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - const bool preferAtrace = internal::shouldPreferAtrace(perfettoTeCategory, category); - const bool preferPerfetto = internal::isPerfettoCategoryEnabled(perfettoTeCategory); + const bool preferAtrace = + internal::shouldPreferAtrace(perfettoTeCategory, category); + const bool preferPerfetto = + internal::isPerfettoCategoryEnabled(perfettoTeCategory); if (CC_LIKELY(!(preferAtrace || preferPerfetto))) { return; } @@ -158,7 +164,7 @@ void traceFormatInstant(uint64_t category, const char* fmt, ...) { } void traceInstantForTrack(uint64_t category, const char* trackName, - const char* name) { + const char* name) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); @@ -181,20 +187,21 @@ void traceCounter(uint64_t category, const char* name, int64_t value) { } void traceCounter32(uint64_t category, const char* name, int32_t value) { - struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); if (internal::shouldPreferAtrace(perfettoTeCategory, category)) { atrace_int(category, name, value); } else if (internal::isPerfettoCategoryEnabled(perfettoTeCategory)) { internal::perfettoTraceCounter(*perfettoTeCategory, name, - static_cast<int64_t>(value)); + static_cast<int64_t>(value)); } } bool isTagEnabled(uint64_t category) { struct PerfettoTeCategory* perfettoTeCategory = internal::toPerfettoCategory(category); - return internal::isPerfettoCategoryEnabled(perfettoTeCategory) - || atrace_is_tag_enabled(category); + return internal::isPerfettoCategoryEnabled(perfettoTeCategory) || + atrace_is_tag_enabled(category); } } // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp index a58bc77131..f92f6df990 100644 --- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp +++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp @@ -47,7 +47,6 @@ #include <atomic> #include <mutex> -#include <android_os.h> #include <android-base/properties.h> #include <cutils/trace.h> #include <inttypes.h> @@ -228,10 +227,6 @@ struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) { } void registerWithPerfetto(bool test) { - if (!android::os::perfetto_sdk_tracing()) { - return; - } - static std::once_flag registration; std::call_once(registration, [test]() { struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT(); diff --git a/libs/tracing_perfetto/tracing_sdk.cpp b/libs/tracing_perfetto/tracing_sdk.cpp new file mode 100644 index 0000000000..02e8d10aa4 --- /dev/null +++ b/libs/tracing_perfetto/tracing_sdk.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2024 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 "tracing_sdk.h" + +#include <android-base/logging.h> +#include <cutils/trace.h> + +#include <cstdarg> +#include <cstdlib> + +#include "perfetto/public/abi/producer_abi.h" +#include "perfetto/public/te_category_macros.h" +#include "perfetto/public/te_macros.h" +#include "perfetto/public/track_event.h" +#include "tracing_perfetto.h" + +namespace tracing_perfetto { +void trace_event(int type, const PerfettoTeCategory* perfettoTeCategory, + const char* name, tracing_perfetto::Extra* extra) { + bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT( + perfettoTeCategory->enabled, PERFETTO_MEMORY_ORDER_RELAXED)); + if (enabled) { + extra->push_extra(nullptr); + PerfettoTeHlEmitImpl(perfettoTeCategory->impl, type, + type == PERFETTO_TE_TYPE_COUNTER ? nullptr : name, + extra->get()); + extra->pop_extra(); + } +} + +uint64_t get_process_track_uuid() { + return PerfettoTeProcessTrackUuid(); +} + +uint64_t get_thread_track_uuid(pid_t tid) { + // Cating a signed pid_t to unsigned + return PerfettoTeProcessTrackUuid() ^ PERFETTO_STATIC_CAST(uint64_t, tid); +} + +Extra::Extra() { +} + +void Extra::push_extra(PerfettoTeHlExtra* ptr) { + extras_.push_back(ptr); +} + +void Extra::pop_extra() { + extras_.pop_back(); +} + +void Extra::clear_extras() { + extras_.clear(); +} + +void Extra::delete_extra(Extra* ptr) { + delete ptr; +} + +PerfettoTeHlExtra* const* Extra::get() const { + return extras_.data(); +} + +Category::Category(const std::string& name, const std::string& tag, + const std::string& severity) + : category_({.enabled = &perfetto_atomic_false}), + name_(name), + tag_(tag), + severity_(severity) { +} + +Category::~Category() { + unregister_category(); +} + +void Category::register_category() { + if (category_.impl) return; + + std::vector<const char*> tags; + if (!tag_.empty()) tags.push_back(tag_.data()); + if (!severity_.empty()) tags.push_back(severity_.data()); + + category_.desc = {name_.c_str(), name_.c_str(), tags.data(), tags.size()}; + + PerfettoTeCategoryRegister(&category_); + PerfettoTePublishCategories(); +} + +void Category::unregister_category() { + if (!category_.impl) return; + + PerfettoTeCategoryUnregister(&category_); + PerfettoTePublishCategories(); +} + +bool Category::is_category_enabled() { + return PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT( + (category_).enabled, PERFETTO_MEMORY_ORDER_RELAXED)); +} + +const PerfettoTeCategory* Category::get() const { + return &category_; +} + +void Category::delete_category(Category* ptr) { + delete ptr; +} + +Flow::Flow() : flow_{} { +} + +void Flow::set_process_flow(uint64_t id) { + flow_.header.type = PERFETTO_TE_HL_EXTRA_TYPE_FLOW; + PerfettoTeFlow ret = PerfettoTeProcessScopedFlow(id); + flow_.id = ret.id; +} + +void Flow::set_process_terminating_flow(uint64_t id) { + flow_.header.type = PERFETTO_TE_HL_EXTRA_TYPE_TERMINATING_FLOW; + PerfettoTeFlow ret = PerfettoTeProcessScopedFlow(id); + flow_.id = ret.id; +} + +const PerfettoTeHlExtraFlow* Flow::get() const { + return &flow_; +} + +void Flow::delete_flow(Flow* ptr) { + delete ptr; +} + +NamedTrack::NamedTrack(uint64_t id, uint64_t parent_uuid, + const std::string& name) + : name_(name), + track_{{PERFETTO_TE_HL_EXTRA_TYPE_NAMED_TRACK}, + name_.data(), + id, + parent_uuid} { +} + +const PerfettoTeHlExtraNamedTrack* NamedTrack::get() const { + return &track_; +} + +void NamedTrack::delete_track(NamedTrack* ptr) { + delete ptr; +} + +RegisteredTrack::RegisteredTrack(uint64_t id, uint64_t parent_uuid, + const std::string& name, bool is_counter) + : registered_track_{}, + track_{{PERFETTO_TE_HL_EXTRA_TYPE_REGISTERED_TRACK}, + &(registered_track_.impl)}, + name_(name), + id_(id), + parent_uuid_(parent_uuid), + is_counter_(is_counter) { + register_track(); +} + +RegisteredTrack::~RegisteredTrack() { + unregister_track(); +} + +void RegisteredTrack::register_track() { + if (registered_track_.impl.descriptor) return; + + if (is_counter_) { + PerfettoTeCounterTrackRegister(®istered_track_, name_.data(), + parent_uuid_); + } else { + PerfettoTeNamedTrackRegister(®istered_track_, name_.data(), id_, + parent_uuid_); + } +} + +void RegisteredTrack::unregister_track() { + if (!registered_track_.impl.descriptor) return; + PerfettoTeRegisteredTrackUnregister(®istered_track_); +} + +const PerfettoTeHlExtraRegisteredTrack* RegisteredTrack::get() const { + return &track_; +} + +void RegisteredTrack::delete_track(RegisteredTrack* ptr) { + delete ptr; +} + +Proto::Proto() : proto_({PERFETTO_TE_HL_EXTRA_TYPE_PROTO_FIELDS}, nullptr) { +} + +void Proto::add_field(PerfettoTeHlProtoField* ptr) { + if (!fields_.empty()) { + fields_.pop_back(); + } + + fields_.push_back(ptr); + fields_.push_back(nullptr); + proto_.fields = fields_.data(); +} + +void Proto::clear_fields() { + fields_.clear(); + proto_.fields = nullptr; +} + +void Proto::delete_proto(Proto* ptr) { + delete ptr; +} + +const PerfettoTeHlExtraProtoFields* Proto::get() const { + return &proto_; +} + +ProtoFieldNested::ProtoFieldNested() + : field_({PERFETTO_TE_HL_PROTO_TYPE_NESTED}, nullptr) { +} + +void ProtoFieldNested::add_field(PerfettoTeHlProtoField* ptr) { + if (!fields_.empty()) { + fields_.pop_back(); + } + + fields_.push_back(ptr); + fields_.push_back(nullptr); + field_.fields = fields_.data(); +} + +void ProtoFieldNested::set_id(uint32_t id) { + fields_.clear(); + field_.header.id = id; + field_.fields = nullptr; +} + +void ProtoFieldNested::delete_field(ProtoFieldNested* ptr) { + delete ptr; +} + +const PerfettoTeHlProtoFieldNested* ProtoFieldNested::get() const { + return &field_; +} + +void activate_trigger(const char* name, uint32_t ttl_ms) { + const char* names[] = {name, nullptr}; + PerfettoProducerActivateTriggers(names, ttl_ms); +} +} // namespace tracing_perfetto diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java index 5c12323633..fc0aef4964 100644 --- a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java +++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java @@ -39,29 +39,9 @@ import java.util.stream.Collectors; @RunWith(DeviceJUnit4ClassRunner.class) public class GpuWorkTracepointTest extends BaseHostJUnit4Test { - private static final String CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH = - "/sys/kernel/tracing/events/power/cpu_frequency/format"; private static final String GPU_WORK_PERIOD_TRACEPOINT_FORMAT_PATH = "/sys/kernel/tracing/events/power/gpu_work_period/format"; - @Test - public void testReadTracingEvents() throws Exception { - // Test |testGpuWorkPeriodTracepointFormat| is dependent on whether certain tracepoint - // paths exist. This means the test will vacuously pass if the tracepoint file system is - // inaccessible. Thus, as a basic check, we make sure the CPU frequency tracepoint format - // is accessible. If not, something is probably fundamentally broken about the tracing - // file system. - CommandResult commandResult = getDevice().executeShellV2Command( - String.format("cat %s", CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH)); - - assertEquals(String.format( - "Failed to read \"%s\". This probably means that the tracing file system " - + "is fundamentally broken in some way, possibly due to bad " - + "permissions.", - CPU_FREQUENCY_TRACEPOINT_FORMAT_PATH), - commandResult.getStatus(), CommandStatus.SUCCESS); - } - @VsrTest(requirements={"VSR-3.3-004"}) @RequiresDevice @Test diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 811692f1bf..5bf6ebbb46 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -98,6 +98,22 @@ std::unordered_set<ui::LogicalDisplayId> getPrivacySensitiveDisplaysFromWindowIn return privacySensitiveDisplays; } +vec2 calculatePositionOnDestinationViewport(const DisplayViewport& destinationViewport, + float pointerOffset, + DisplayTopologyPosition sourceBoundary) { + // destination is opposite of the source boundary + switch (sourceBoundary) { + case DisplayTopologyPosition::RIGHT: + return {0, pointerOffset}; // left edge + case DisplayTopologyPosition::TOP: + return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge + case DisplayTopologyPosition::LEFT: + return {destinationViewport.logicalRight, pointerOffset}; // right edge + case DisplayTopologyPosition::BOTTOM: + return {pointerOffset, 0}; // top edge + } +} + } // namespace // --- PointerChoreographer --- @@ -327,19 +343,19 @@ void PointerChoreographer::handleUnconsumedDeltaLocked(PointerControllerInterfac // except sometimes near the corners. // In these cases this behaviour is not noticeable. We also do not apply unconsumed delta on // the destination display for the same reason. - DisplayPosition sourceBoundary; + DisplayTopologyPosition sourceBoundary; float cursorOffset = 0.0f; if (rotatedUnconsumedDelta.x > 0) { - sourceBoundary = DisplayPosition::RIGHT; + sourceBoundary = DisplayTopologyPosition::RIGHT; cursorOffset = rotatedCursorPosition.y; } else if (rotatedUnconsumedDelta.x < 0) { - sourceBoundary = DisplayPosition::LEFT; + sourceBoundary = DisplayTopologyPosition::LEFT; cursorOffset = rotatedCursorPosition.y; } else if (rotatedUnconsumedDelta.y > 0) { - sourceBoundary = DisplayPosition::BOTTOM; + sourceBoundary = DisplayTopologyPosition::BOTTOM; cursorOffset = rotatedCursorPosition.x; } else { - sourceBoundary = DisplayPosition::TOP; + sourceBoundary = DisplayTopologyPosition::TOP; cursorOffset = rotatedCursorPosition.x; } @@ -369,8 +385,9 @@ void PointerChoreographer::handleUnconsumedDeltaLocked(PointerControllerInterfac pc.fade(PointerControllerInterface::Transition::IMMEDIATE); pc.setDisplayViewport(destinationViewport); vec2 destinationPosition = - calculateDestinationPosition(destinationViewport, cursorOffset - destinationOffset, - sourceBoundary); + calculatePositionOnDestinationViewport(destinationViewport, + cursorOffset - destinationOffset, + sourceBoundary); // Transform position back to un-rotated coordinate space before sending it to controller destinationPosition = pc.getDisplayTransform().inverse().transform(destinationPosition.x, @@ -379,22 +396,6 @@ void PointerChoreographer::handleUnconsumedDeltaLocked(PointerControllerInterfac pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); } -vec2 PointerChoreographer::calculateDestinationPosition(const DisplayViewport& destinationViewport, - float pointerOffset, - DisplayPosition sourceBoundary) { - // destination is opposite of the source boundary - switch (sourceBoundary) { - case DisplayPosition::RIGHT: - return {0, pointerOffset}; // left edge - case DisplayPosition::TOP: - return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge - case DisplayPosition::LEFT: - return {destinationViewport.logicalRight, pointerOffset}; // right edge - case DisplayPosition::BOTTOM: - return {pointerOffset, 0}; // top edge - } -} - void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) { if (args.displayId == ui::LogicalDisplayId::INVALID) { return; @@ -540,8 +541,7 @@ void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) } void PointerChoreographer::onControllerAddedOrRemovedLocked() { - if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows() && - !com::android::input::flags::connected_displays_cursor()) { + if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) { return; } bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() || @@ -607,11 +607,16 @@ void PointerChoreographer::notifyPointerCaptureChanged( mNextListener.notify(args); } -void PointerChoreographer::setDisplayTopology( - const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>& - displayTopology) { +void PointerChoreographer::setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) { std::scoped_lock _l(getLock()); - mTopology = displayTopology; + mTopology = displayTopologyGraph; + + // make primary display default mouse display, if it was not set + // or the existing display was removed + if (mDefaultMouseDisplayId == ui::LogicalDisplayId::INVALID || + mTopology.graph.find(mDefaultMouseDisplayId) != mTopology.graph.end()) { + mDefaultMouseDisplayId = mTopology.primaryDisplayId; + } } void PointerChoreographer::dump(std::string& dump) { @@ -985,73 +990,17 @@ PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusContr return ConstructorDelegate(std::move(ctor)); } -void PointerChoreographer::populateFakeDisplayTopologyLocked( - const std::vector<gui::DisplayInfo>& displayInfos) { - if (!com::android::input::flags::connected_displays_cursor()) { - return; - } - - if (displayInfos.size() == mTopology.size()) { - bool displaysChanged = false; - for (const auto& displayInfo : displayInfos) { - if (mTopology.find(displayInfo.displayId) == mTopology.end()) { - displaysChanged = true; - break; - } - } - - if (!displaysChanged) { - return; - } - } - - // create a fake topology assuming following order - // default-display (top-edge) -> next-display (right-edge) -> next-display (right-edge) ... - // This also adds a 100px offset on corresponding edge for better manual testing - // ┌────────┐ - // │ next ├─────────┐ - // ┌─└───────┐┤ next 2 │ ... - // │ default │└─────────┘ - // └─────────┘ - mTopology.clear(); - - // treat default display as base, in real topology it should be the primary-display - ui::LogicalDisplayId previousDisplay = ui::LogicalDisplayId::DEFAULT; - for (const auto& displayInfo : displayInfos) { - if (displayInfo.displayId == ui::LogicalDisplayId::DEFAULT) { - continue; - } - if (previousDisplay == ui::LogicalDisplayId::DEFAULT) { - mTopology[previousDisplay].push_back( - {displayInfo.displayId, DisplayPosition::TOP, 100}); - mTopology[displayInfo.displayId].push_back( - {previousDisplay, DisplayPosition::BOTTOM, -100}); - } else { - mTopology[previousDisplay].push_back( - {displayInfo.displayId, DisplayPosition::RIGHT, 100}); - mTopology[displayInfo.displayId].push_back( - {previousDisplay, DisplayPosition::LEFT, -100}); - } - previousDisplay = displayInfo.displayId; - } - - // update default pointer display. In real topology it should be the primary-display - if (mTopology.find(mDefaultMouseDisplayId) == mTopology.end()) { - mDefaultMouseDisplayId = ui::LogicalDisplayId::DEFAULT; - } -} - std::optional<std::pair<const DisplayViewport*, float /*offset*/>> PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId, - const DisplayPosition sourceBoundary, + const DisplayTopologyPosition sourceBoundary, float cursorOffset) const { - const auto& sourceNode = mTopology.find(sourceDisplayId); - if (sourceNode == mTopology.end()) { + const auto& sourceNode = mTopology.graph.find(sourceDisplayId); + if (sourceNode == mTopology.graph.end()) { // Topology is likely out of sync with viewport info, wait for it to be updated LOG(WARNING) << "Source display missing from topology " << sourceDisplayId; return std::nullopt; } - for (const AdjacentDisplay& adjacentDisplay : sourceNode->second) { + for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : sourceNode->second) { if (adjacentDisplay.position != sourceBoundary) { continue; } @@ -1064,8 +1013,8 @@ PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId so continue; } // target position must be within target display boundary - const int32_t edgeSize = - sourceBoundary == DisplayPosition::TOP || sourceBoundary == DisplayPosition::BOTTOM + const int32_t edgeSize = sourceBoundary == DisplayTopologyPosition::TOP || + sourceBoundary == DisplayTopologyPosition::BOTTOM ? (destinationViewport->logicalRight - destinationViewport->logicalLeft) : (destinationViewport->logicalBottom - destinationViewport->logicalTop); if (cursorOffset >= adjacentDisplay.offsetPx && @@ -1093,7 +1042,6 @@ void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfo mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays); mPointerChoreographer->onPrivacySensitiveDisplaysChangedLocked(mPrivacySensitiveDisplays); } - mPointerChoreographer->populateFakeDisplayTopologyLocked(windowInfosUpdate.displayInfos); } void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfosLocked( diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index 939529f774..c2f5ec0e87 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -22,6 +22,7 @@ #include <android-base/thread_annotations.h> #include <gui/WindowInfosListener.h> +#include <input/DisplayTopologyGraph.h> #include <type_traits> #include <unordered_set> @@ -80,6 +81,11 @@ public: */ virtual void setFocusedDisplay(ui::LogicalDisplayId displayId) = 0; + /* + * Used by InputManager to notify changes in the DisplayTopology + */ + virtual void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) = 0; + /** * This method may be called on any thread (usually by the input manager on a binder thread). */ @@ -103,6 +109,7 @@ public: ui::LogicalDisplayId displayId, DeviceId deviceId) override; void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override; void setFocusedDisplay(ui::LogicalDisplayId displayId) override; + void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph); void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; void notifyKey(const NotifyKeyArgs& args) override; @@ -113,24 +120,6 @@ public: void notifyDeviceReset(const NotifyDeviceResetArgs& args) override; void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override; - // TODO(b/362719483) remove these when real topology is available - enum class DisplayPosition { - RIGHT, - TOP, - LEFT, - BOTTOM, - ftl_last = BOTTOM, - }; - - struct AdjacentDisplay { - ui::LogicalDisplayId displayId; - DisplayPosition position; - float offsetPx; - }; - void setDisplayTopology( - const std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>>& - displayTopology); - void dump(std::string& dump) override; private: @@ -174,18 +163,16 @@ private: void handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const vec2& unconsumedDelta) REQUIRES(getLock()); - void populateFakeDisplayTopologyLocked(const std::vector<gui::DisplayInfo>& displayInfos) - REQUIRES(getLock()); - std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked( - const ui::LogicalDisplayId sourceDisplayId, const DisplayPosition sourceBoundary, - float cursorOffset) const REQUIRES(getLock()); - - static vec2 calculateDestinationPosition(const DisplayViewport& destinationViewport, - float pointerOffset, DisplayPosition sourceBoundary); + const ui::LogicalDisplayId sourceDisplayId, + const DisplayTopologyPosition sourceBoundary, float cursorOffset) const + REQUIRES(getLock()); - std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>> mTopology - GUARDED_BY(getLock()); + /* Topology is initialized with default-constructed value, which is an empty topology. Till we + * receive setDisplayTopology call. + * Meanwhile Choreographer will treat every display as independent disconnected display. + */ + DisplayTopologyGraph mTopology GUARDED_BY(getLock()); /* This listener keeps tracks of visible privacy sensitive displays and updates the * choreographer if there are any changes. diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 4d6b6c7fd5..42b269e651 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -279,7 +279,10 @@ struct InputReaderConfiguration { static_cast<float>( android::os::IInputConstants:: DEFAULT_POINTER_ACCELERATION)), - wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f), + wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, + static_cast<float>( + android::os::IInputConstants:: + DEFAULT_MOUSE_WHEEL_ACCELERATION)), pointerGesturesEnabled(true), pointerGestureQuietInterval(100 * 1000000LL), // 100 ms pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 27da3d33f9..1ca2998f6b 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -2633,16 +2633,14 @@ protected: ui::ROTATION_0), }; - std::unordered_map<ui::LogicalDisplayId, std::vector<PointerChoreographer::AdjacentDisplay>> - mTopology{ - {DISPLAY_CENTER_ID, - {{DISPLAY_TOP_ID, PointerChoreographer::DisplayPosition::TOP, 10.0f}, - {DISPLAY_RIGHT_ID, PointerChoreographer::DisplayPosition::RIGHT, 10.0f}, - {DISPLAY_BOTTOM_ID, PointerChoreographer::DisplayPosition::BOTTOM, 10.0f}, - {DISPLAY_LEFT_ID, PointerChoreographer::DisplayPosition::LEFT, 10.0f}, - {DISPLAY_TOP_RIGHT_CORNER_ID, PointerChoreographer::DisplayPosition::RIGHT, - -90.0f}}}, - }; + DisplayTopologyGraph mTopology{DISPLAY_CENTER_ID, + {{DISPLAY_CENTER_ID, + {{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 10.0f}, + {DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f}, + {DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f}, + {DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f}, + {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT, + -90.0f}}}}}; private: DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height, @@ -2706,6 +2704,11 @@ INSTANTIATE_TEST_SUITE_P( testing::Values( // Note: Upon viewport transition cursor will be positioned at the boundary of the // destination, as we drop any unconsumed delta. + std::make_tuple("PrimaryDisplayIsDefault", AINPUT_SOURCE_MOUSE, + ControllerType::MOUSE, ToolType::MOUSE, + vec2(50, 50) /* initial x/y */, vec2(0, 0) /* delta x/y */, + PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, + vec2(50, 50) /* destination x/y */), std::make_tuple("UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, ToolType::MOUSE, vec2(50, 50) /* initial x/y */, vec2(25, 25) /* delta x/y */, diff --git a/services/surfaceflinger/ActivePictureUpdater.cpp b/services/surfaceflinger/ActivePictureTracker.cpp index 210e948a49..4e6fa66ed8 100644 --- a/services/surfaceflinger/ActivePictureUpdater.cpp +++ b/services/surfaceflinger/ActivePictureTracker.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ActivePictureUpdater.h" +#include "ActivePictureTracker.h" #include <algorithm> @@ -23,7 +23,10 @@ namespace android { -void ActivePictureUpdater::onLayerComposed(const Layer& layer, const LayerFE& layerFE, +using gui::ActivePicture; +using gui::IActivePictureListener; + +void ActivePictureTracker::onLayerComposed(const Layer& layer, const LayerFE& layerFE, const CompositionResult& result) { if (result.wasPictureProfileCommitted) { gui::ActivePicture picture; @@ -39,10 +42,47 @@ void ActivePictureUpdater::onLayerComposed(const Layer& layer, const LayerFE& la } } -bool ActivePictureUpdater::updateAndHasChanged() { +void ActivePictureTracker::updateAndNotifyListeners(const Listeners& listenersToAdd, + const Listeners& listenersToRemove) { + Listeners newListeners = updateListeners(listenersToAdd, listenersToRemove); + if (updateAndHasChanged()) { + for (auto listener : mListeners) { + listener->onActivePicturesChanged(getActivePictures()); + } + } else { + for (auto listener : newListeners) { + listener->onActivePicturesChanged(getActivePictures()); + } + } +} + +ActivePictureTracker::Listeners ActivePictureTracker::updateListeners( + const Listeners& listenersToAdd, const Listeners& listenersToRemove) { + Listeners newListeners; + for (auto listener : listenersToRemove) { + std::erase_if(mListeners, [listener](const sp<IActivePictureListener>& otherListener) { + return IInterface::asBinder(listener) == IInterface::asBinder(otherListener); + }); + } + for (auto listener : listenersToAdd) { + if (std::find_if(mListeners.begin(), mListeners.end(), + [listener](const sp<IActivePictureListener>& otherListener) { + return IInterface::asBinder(listener) == + IInterface::asBinder(otherListener); + }) == mListeners.end()) { + newListeners.push_back(listener); + } + } + for (auto listener : newListeners) { + mListeners.push_back(listener); + } + return newListeners; +} + +bool ActivePictureTracker::updateAndHasChanged() { bool hasChanged = true; if (mNewActivePictures.size() == mOldActivePictures.size()) { - auto compare = [](const gui::ActivePicture& lhs, const gui::ActivePicture& rhs) -> int { + auto compare = [](const ActivePicture& lhs, const ActivePicture& rhs) -> int { if (lhs.layerId == rhs.layerId) { return lhs.pictureProfileId < rhs.pictureProfileId; } @@ -59,7 +99,7 @@ bool ActivePictureUpdater::updateAndHasChanged() { return hasChanged; } -const std::vector<gui::ActivePicture>& ActivePictureUpdater::getActivePictures() const { +const std::vector<ActivePicture>& ActivePictureTracker::getActivePictures() const { return mOldActivePictures; } diff --git a/services/surfaceflinger/ActivePictureUpdater.h b/services/surfaceflinger/ActivePictureTracker.h index 20779bb0ff..cb319a58be 100644 --- a/services/surfaceflinger/ActivePictureUpdater.h +++ b/services/surfaceflinger/ActivePictureTracker.h @@ -19,6 +19,7 @@ #include <vector> #include <android/gui/ActivePicture.h> +#include <android/gui/IActivePictureListener.h> namespace android { @@ -27,21 +28,28 @@ class LayerFE; struct CompositionResult; // Keeps track of active pictures - layers that are undergoing picture processing. -class ActivePictureUpdater { +class ActivePictureTracker { public: + typedef std::vector<sp<gui::IActivePictureListener>> Listeners; + // Called for each visible layer when SurfaceFlinger finishes composing. void onLayerComposed(const Layer& layer, const LayerFE& layerFE, const CompositionResult& result); // Update internals and return whether the set of active pictures have changed. - bool updateAndHasChanged(); + void updateAndNotifyListeners(const Listeners& activePictureListenersToAdd, + const Listeners& activePictureListenersToRemove); // The current set of active pictures. const std::vector<gui::ActivePicture>& getActivePictures() const; private: + Listeners updateListeners(const Listeners& listenersToAdd, const Listeners& listenersToRemove); + bool updateAndHasChanged(); + std::vector<gui::ActivePicture> mOldActivePictures; std::vector<gui::ActivePicture> mNewActivePictures; + Listeners mListeners; }; } // namespace android diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 7b90363763..05f7d1ba21 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -67,6 +67,7 @@ cc_defaults { "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", "android.hardware.graphics.composer@2.4", + "android.os.flags-aconfig-cc-host", "libbase", "libbinder", "libbinder_ndk", @@ -198,7 +199,7 @@ filegroup { name: "libsurfaceflinger_sources", srcs: [ ":libsurfaceflinger_backend_sources", - "ActivePictureUpdater.cpp", + "ActivePictureTracker.cpp", "BackgroundExecutor.cpp", "Client.cpp", "ClientCache.cpp", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h index 2e7a7d9c5a..c1b864df02 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h @@ -118,7 +118,8 @@ public: // isPeekingThrough specifies whether this layer will be shown through a // hole punch in a layer above it. virtual void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, - bool zIsOverridden, bool isPeekingThrough) = 0; + bool zIsOverridden, bool isPeekingThrough, + bool isLutSupported) = 0; // Updates the cursor position with the HWC virtual void writeCursorPositionToHWC() const = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index 712b55123f..0063eee0e1 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -58,7 +58,7 @@ public: const std::optional<std::vector<std::optional<LutProperties>>> properties = std::nullopt) override; void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden, - bool isPeekingThrough) override; + bool isPeekingThrough, bool hasLutsProperties) override; void writeCursorPositionToHWC() const override; HWC2::Layer* getHwcLayer() const override; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h index 9333ebb8cd..09c47f0224 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h @@ -47,7 +47,7 @@ public: (bool, bool, ui::Transform::RotationFlags, (const std::optional<std::vector<std::optional< aidl::android::hardware::graphics::composer3::LutProperties>>>))); - MOCK_METHOD5(writeStateToHWC, void(bool, bool, uint32_t, bool, bool)); + MOCK_METHOD(void, writeStateToHWC, (bool, bool, uint32_t, bool, bool, bool)); MOCK_CONST_METHOD0(writeCursorPositionToHWC, void()); MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*()); diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index f9ed92d1ee..734d76404e 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -810,7 +810,7 @@ void Output::commitPictureProfilesToCompositionState() { } auto compare = [](const ::android::compositionengine::OutputLayer* lhs, const ::android::compositionengine::OutputLayer* rhs) { - return lhs->getPictureProfilePriority() > rhs->getPictureProfilePriority(); + return lhs->getPictureProfilePriority() < rhs->getPictureProfilePriority(); }; std::priority_queue<::android::compositionengine::OutputLayer*, std::vector<::android::compositionengine::OutputLayer*>, decltype(compare)> @@ -909,6 +909,9 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr applyPictureProfile(); + auto* properties = getOverlaySupport(); + bool hasLutsProperties = properties && properties->lutProperties.has_value(); + compositionengine::OutputLayer* peekThroughLayer = nullptr; sp<GraphicBuffer> previousOverride = nullptr; bool includeGeometry = refreshArgs.updatingGeometryThisFrame; @@ -940,7 +943,7 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr includeGeometry = true; constexpr bool isPeekingThrough = true; peekThroughLayer->writeStateToHWC(includeGeometry, false, z++, overrideZ, - isPeekingThrough); + isPeekingThrough, hasLutsProperties); outputLayerHash ^= android::hashCombine( reinterpret_cast<uint64_t>(&peekThroughLayer->getLayerFE()), z, includeGeometry, overrideZ, isPeekingThrough, @@ -952,7 +955,8 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr } constexpr bool isPeekingThrough = false; - layer->writeStateToHWC(includeGeometry, skipLayer, z++, overrideZ, isPeekingThrough); + layer->writeStateToHWC(includeGeometry, skipLayer, z++, overrideZ, isPeekingThrough, + hasLutsProperties); if (!skipLayer) { outputLayerHash ^= android::hashCombine( reinterpret_cast<uint64_t>(&layer->getLayerFE()), diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index a040c88e78..96b86d5a31 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -449,7 +449,8 @@ void OutputLayer::commitPictureProfileToCompositionState() { } void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, - bool zIsOverridden, bool isPeekingThrough) { + bool zIsOverridden, bool isPeekingThrough, + bool hasLutsProperties) { const auto& state = getState(); // Skip doing this if there is no HWC interface if (!state.hwc) { @@ -491,8 +492,9 @@ void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough, skipLayer); - - writeLutToHWC(hwcLayer.get(), *outputIndependentState); + if (hasLutsProperties) { + writeLutToHWC(hwcLayer.get(), *outputIndependentState); + } if (requestedCompositionType == Composition::SOLID_COLOR) { writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState); @@ -589,29 +591,30 @@ void OutputLayer::writeOutputIndependentGeometryStateToHWC( void OutputLayer::writeLutToHWC(HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) { - if (!outputIndependentState.luts) { - return; - } - auto& lutFileDescriptor = outputIndependentState.luts->getLutFileDescriptor(); - auto lutOffsets = outputIndependentState.luts->offsets; - auto& lutProperties = outputIndependentState.luts->lutProperties; + Luts luts; + // if outputIndependentState.luts is nullptr, it means we want to clear the LUTs + // and we pass an empty Luts object to the HWC. + if (outputIndependentState.luts) { + auto& lutFileDescriptor = outputIndependentState.luts->getLutFileDescriptor(); + auto lutOffsets = outputIndependentState.luts->offsets; + auto& lutProperties = outputIndependentState.luts->lutProperties; + + std::vector<LutProperties> aidlProperties; + aidlProperties.reserve(lutProperties.size()); + for (size_t i = 0; i < lutOffsets.size(); i++) { + aidlProperties.emplace_back( + LutProperties{.dimension = static_cast<LutProperties::Dimension>( + lutProperties[i].dimension), + .size = lutProperties[i].size, + .samplingKeys = {static_cast<LutProperties::SamplingKey>( + lutProperties[i].samplingKey)}}); + } - std::vector<LutProperties> aidlProperties; - aidlProperties.reserve(lutProperties.size()); - for (size_t i = 0; i < lutOffsets.size(); i++) { - LutProperties properties; - properties.dimension = static_cast<LutProperties::Dimension>(lutProperties[i].dimension); - properties.size = lutProperties[i].size; - properties.samplingKeys = { - static_cast<LutProperties::SamplingKey>(lutProperties[i].samplingKey)}; - aidlProperties.emplace_back(properties); + luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get())); + luts.offsets = lutOffsets; + luts.lutProperties = std::move(aidlProperties); } - Luts luts; - luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get())); - luts.offsets = lutOffsets; - luts.lutProperties = std::move(aidlProperties); - switch (auto error = hwcLayer->setLuts(luts)) { case hal::Error::NONE: break; diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index dbffe80a3a..ca262ee16a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -541,6 +541,9 @@ struct OutputLayerPartialMockForUpdateCompositionState : public impl::OutputLaye MOCK_CONST_METHOD1(calculateOutputSourceCrop, FloatRect(uint32_t)); MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect()); MOCK_CONST_METHOD1(calculateOutputRelativeBufferTransform, uint32_t(uint32_t)); + MOCK_METHOD(void, updateLuts, + (const LayerFECompositionState&, + const std::optional<std::vector<std::optional<LutProperties>>>&)); // compositionengine::OutputLayer overrides const compositionengine::Output& getOutput() const override { return mOutput; } @@ -985,21 +988,24 @@ TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) { EXPECT_CALL(mLayerFE, getCompositionState()).WillOnce(Return(nullptr)); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) { mOutputLayer.editState().hwc.reset(); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) { mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) { @@ -1010,7 +1016,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) { EXPECT_CALL(mLayerFE, hasRoundedCorners()).WillOnce(Return(false)); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) { @@ -1041,7 +1048,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) { expectSetColorCall(); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) { @@ -1052,7 +1060,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) { expectSetCompositionTypeCall(Composition::SIDEBAND); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) { @@ -1063,7 +1072,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) { expectSetCompositionTypeCall(Composition::CURSOR); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) { @@ -1074,7 +1084,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) { expectSetCompositionTypeCall(Composition::DEVICE); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) { @@ -1087,7 +1098,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) { expectNoSetCompositionTypeCall(); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) { @@ -1098,7 +1110,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransf expectSetCompositionTypeCall(Composition::CLIENT); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) { @@ -1111,7 +1124,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompo expectSetCompositionTypeCall(Composition::CLIENT); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) { @@ -1125,7 +1139,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) { expectSetCompositionTypeCall(Composition::DEVICE); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) { @@ -1137,7 +1152,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPres expectSetCompositionTypeCall(Composition::DEVICE); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerDoesNotSendBuffer) { @@ -1152,7 +1168,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerDoesNotSendBuffer) { expectSetCompositionTypeCall(Composition::DEVICE); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerForSolidColorDoesNotSendBuffer) { @@ -1167,7 +1184,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerForSolidColorDoesNotSe expectSetCompositionTypeCall(Composition::DEVICE); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) { @@ -1182,7 +1200,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) { expectSetCompositionTypeCall(Composition::DEVICE); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoForSolidColorIfPresent) { @@ -1197,7 +1216,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoForSolidColorIfPresen expectSetCompositionTypeCall(Composition::DEVICE); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) { @@ -1211,7 +1231,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage expectSetCompositionTypeCall(Composition::DEVICE); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompositionInfo) { @@ -1227,7 +1248,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompos expectSetCompositionTypeCall(Composition::DEVICE); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompositionInfo) { @@ -1244,7 +1266,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompos expectSetCompositionTypeCall(Composition::CLIENT); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) { @@ -1258,7 +1281,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) { expectPerFrameCommonCalls(); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, isPeekingThroughSetsOverride) { @@ -1266,7 +1290,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, isPeekingThroughSetsOverride) { expectPerFrameCommonCalls(); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ true); + /*zIsOverridden*/ false, /*isPeekingThrough*/ true, + /*hasLutsProperties*/ false); EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden); } @@ -1276,7 +1301,7 @@ TEST_F(OutputLayerWriteStateToHWCTest, zIsOverriddenSetsOverride) { mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, /*zIsOverridden*/ true, /*isPeekingThrough*/ - false); + false, /*hasLutsProperties*/ false); EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden); } @@ -1288,7 +1313,7 @@ TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersForceClientComposition) { mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, /*zIsOverridden*/ false, /*isPeekingThrough*/ - false); + false, /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersPeekingThroughAllowsDeviceComposition) { @@ -1301,7 +1326,7 @@ TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersPeekingThroughAllowsDeviceC mLayerFEState.compositionType = Composition::DEVICE; mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, /*zIsOverridden*/ false, /*isPeekingThrough*/ - true); + true, /*hasLutsProperties*/ false); EXPECT_EQ(Composition::DEVICE, mOutputLayer.getState().hwc->hwcCompositionType); } @@ -1318,7 +1343,7 @@ TEST_F(OutputLayerWriteStateToHWCTest, setBlockingRegion) { mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, /*zIsOverridden*/ false, /*isPeekingThrough*/ - false); + false, /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, setCompositionTypeRefreshRateIndicator) { @@ -1330,7 +1355,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, setCompositionTypeRefreshRateIndicator) { expectSetCompositionTypeCall(Composition::REFRESH_RATE_INDICATOR); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, setsPictureProfileWhenCommitted) { @@ -1349,7 +1375,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, setsPictureProfileWhenCommitted) { mOutputLayer.commitPictureProfileToCompositionState(); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommitted) { @@ -1367,7 +1394,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommitted) EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(_)).Times(0); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommittedLater) { @@ -1386,7 +1414,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommittedL mOutputLayer.commitPictureProfileToCompositionState(); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); expectGeometryCommonCalls(); expectPerFrameCommonCalls(); @@ -1395,7 +1424,8 @@ TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommittedL EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))).Times(0); // No committing of picture profile before writing the state mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); } /* @@ -1441,21 +1471,24 @@ TEST_F(OutputLayerUncacheBufferTest, canUncacheAndReuseSlot) { mLayerFEState.buffer = kBuffer1; EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 0, kBuffer1, kFence)); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); Mock::VerifyAndClearExpectations(&mHwcLayer); // Buffer2 is stored in slot 1 mLayerFEState.buffer = kBuffer2; EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer2, kFence)); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); Mock::VerifyAndClearExpectations(&mHwcLayer); // Buffer3 is stored in slot 2 mLayerFEState.buffer = kBuffer3; EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 2, kBuffer3, kFence)); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); Mock::VerifyAndClearExpectations(&mHwcLayer); // Buffer2 becomes the active buffer again (with a nullptr) and reuses slot 1 @@ -1463,7 +1496,8 @@ TEST_F(OutputLayerUncacheBufferTest, canUncacheAndReuseSlot) { sp<GraphicBuffer> nullBuffer = nullptr; EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, nullBuffer, kFence)); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); Mock::VerifyAndClearExpectations(&mHwcLayer); // Buffer slots are cleared @@ -1481,7 +1515,8 @@ TEST_F(OutputLayerUncacheBufferTest, canUncacheAndReuseSlot) { mLayerFEState.buffer = kBuffer1; EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer1, kFence)); mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false); Mock::VerifyAndClearExpectations(&mHwcLayer); } diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 442b603ca0..09ad9fa497 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -813,19 +813,22 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers updateCompositionState(false, false, ui::Transform::ROT_180, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180, _)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); injectOutputLayer(layer1); @@ -852,17 +855,20 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerGeometryAndContentF EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); injectOutputLayer(layer1); @@ -888,17 +894,20 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLa EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); injectOutputLayer(layer1); @@ -932,7 +941,8 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, peekThroughLayerChangesOrder) { uint32_t z = 0; EXPECT_CALL(*layer0.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer0.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); // After calling planComposition (which clears overrideInfo), this test sets @@ -942,15 +952,17 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, peekThroughLayerChangesOrder) { EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, /*zIsOverridden*/ true, /*isPeekingThrough*/ - true)); + true, /*hasLutsProperties*/ false)); EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, - /*zIsOverridden*/ true, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ true, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++, - /*zIsOverridden*/ true, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ true, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); injectOutputLayer(layer0); @@ -4962,12 +4974,14 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, noBackgroundBlurWhenOpaque) { EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); layer2.layerFEState.backgroundBlurRadius = 10; @@ -4996,17 +5010,20 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); layer2.layerFEState.backgroundBlurRadius = 10; @@ -5036,17 +5053,20 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) { EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, - /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + /*zIsOverridden*/ false, /*isPeekingThrough*/ false, + /*hasLutsProperties*/ false)); EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); BlurRegion region; @@ -5080,14 +5100,14 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsDisplayProfileBasedOnLay InjectedLayer layer1; injectOutputLayer(layer1); PictureProfileHandle profileForLayer1(1); - EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3)); + EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1)); EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle()) .WillRepeatedly(ReturnRef(profileForLayer1)); InjectedLayer layer2; injectOutputLayer(layer2); PictureProfileHandle profileForLayer2(2); - EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1)); + EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3)); EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle()) .WillRepeatedly(ReturnRef(profileForLayer2)); @@ -5101,13 +5121,13 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsDisplayProfileBasedOnLay // Because StrictMock EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _)); - EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _, _)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _)); - EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _, _)); EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _)); - EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _, _)); // No layer picture profiles should be committed EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0); @@ -5143,14 +5163,14 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsLayerProfileBasedOnLayer InjectedLayer layer1; injectOutputLayer(layer1); PictureProfileHandle profileForLayer1(1); - EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3)); + EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1)); EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle()) .WillRepeatedly(ReturnRef(profileForLayer1)); InjectedLayer layer2; injectOutputLayer(layer2); PictureProfileHandle profileForLayer2(2); - EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1)); + EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3)); EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle()) .WillRepeatedly(ReturnRef(profileForLayer2)); @@ -5164,13 +5184,13 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsLayerProfileBasedOnLayer // Because StrictMock EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _)); - EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _, _)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _)); - EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _, _)); EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _)); - EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _, _)); // The two highest priority layers should have their picture profiles committed EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0); diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 25f6513ffa..8529c72419 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -1678,6 +1678,29 @@ Error AidlComposer::setLayerPictureProfileId(Display display, Layer layer, Pictu return error; } +Error AidlComposer::getLuts(Display display, const std::vector<sp<GraphicBuffer>>& buffers, + std::vector<aidl::android::hardware::graphics::composer3::Luts>* luts) { + std::vector<aidl::android::hardware::graphics::composer3::Buffer> aidlBuffers; + aidlBuffers.reserve(buffers.size()); + + for (auto& buffer : buffers) { + if (buffer.get()) { + aidl::android::hardware::graphics::composer3::Buffer aidlBuffer; + aidlBuffer.handle.emplace(::android::dupToAidl(buffer->getNativeBuffer()->handle)); + aidlBuffers.emplace_back(std::move(aidlBuffer)); + } + } + + const auto status = + mAidlComposerClient->getLuts(translate<int64_t>(display), aidlBuffers, luts); + if (!status.isOk()) { + ALOGE("getLuts failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + + return Error::NONE; +} + ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display) REQUIRES_SHARED(mMutex) { return mWriters.get(display); diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index 6b5ebc59a3..82006f407b 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -245,6 +245,8 @@ public: Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override; Error setDisplayPictureProfileId(Display, PictureProfileId id) override; Error setLayerPictureProfileId(Display, Layer, PictureProfileId id) override; + Error getLuts(Display, const std::vector<sp<GraphicBuffer>>&, + std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override; private: // Many public functions above simply write a command into the command diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index ff292fa350..6e431bb878 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -46,6 +46,7 @@ #include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h> #include <aidl/android/hardware/graphics/composer3/DisplayLuts.h> #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h> +#include <aidl/android/hardware/graphics/composer3/Luts.h> #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> #include <optional> @@ -313,6 +314,8 @@ public: virtual Error getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) = 0; virtual Error setDisplayPictureProfileId(Display display, PictureProfileId id) = 0; virtual Error setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) = 0; + virtual Error getLuts(Display display, const std::vector<sp<GraphicBuffer>>&, + std::vector<V3_0::Luts>*) = 0; }; } // namespace Hwc2 diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 081f4aa269..e63a14bb5e 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -629,7 +629,7 @@ Error Display::getRequestedLuts(LayerLuts* outLuts, auto layer = getLayerById(layerIds[i]); if (layer) { auto& layerLut = tmpLuts[i]; - if (layerLut.luts.pfd.get() > 0 && layerLut.luts.offsets.has_value()) { + if (layerLut.luts.pfd.get() >= 0 && layerLut.luts.offsets.has_value()) { const auto& offsets = layerLut.luts.offsets.value(); std::vector<std::pair<int32_t, LutProperties>> lutOffsetsAndProperties; lutOffsetsAndProperties.reserve(offsets.size()); @@ -669,6 +669,12 @@ Error Display::setPictureProfileHandle(const PictureProfileHandle& handle) { return static_cast<Error>(error); } +Error Display::getLuts(const std::vector<sp<GraphicBuffer>>& buffers, + std::vector<aidl::android::hardware::graphics::composer3::Luts>* outLuts) { + const auto error = mComposer.getLuts(mId, buffers, outLuts); + return static_cast<Error>(error); +} + // For use by Device void Display::setConnected(bool connected) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 6740d8a832..c3deb8429a 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -203,6 +203,9 @@ public: [[nodiscard]] virtual hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) = 0; [[nodiscard]] virtual hal::Error setPictureProfileHandle( const PictureProfileHandle& handle) = 0; + [[nodiscard]] virtual hal::Error getLuts( + const std::vector<android::sp<android::GraphicBuffer>>&, + std::vector<aidl::android::hardware::graphics::composer3::Luts>*) = 0; }; namespace impl { @@ -288,6 +291,8 @@ public: hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override; hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) override; hal::Error setPictureProfileHandle(const android::PictureProfileHandle& handle) override; + hal::Error getLuts(const std::vector<android::sp<android::GraphicBuffer>>&, + std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override; // Other Display methods hal::HWDisplayId getId() const override { return mId; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 721cfd31bb..7f94428617 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -1046,6 +1046,16 @@ status_t HWComposer::setDisplayPictureProfileHandle(PhysicalDisplayId displayId, return NO_ERROR; } +status_t HWComposer::getLuts( + PhysicalDisplayId displayId, const std::vector<sp<GraphicBuffer>>& buffers, + std::vector<aidl::android::hardware::graphics::composer3::Luts>* luts) { + RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; + auto error = hwcDisplay->getLuts(buffers, luts); + RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); + return NO_ERROR; +} + const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const { return mSupportedLayerGenericMetadata; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 52662cffbb..b1b997a197 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -55,6 +55,7 @@ #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> #include <aidl/android/hardware/graphics/composer3/DisplayLuts.h> #include <aidl/android/hardware/graphics/composer3/LutProperties.h> +#include <aidl/android/hardware/graphics/composer3/Luts.h> #include <aidl/android/hardware/graphics/composer3/OutputType.h> #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> @@ -324,6 +325,8 @@ public: virtual int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) = 0; virtual status_t setDisplayPictureProfileHandle(PhysicalDisplayId, const PictureProfileHandle& handle) = 0; + virtual status_t getLuts(PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&, + std::vector<aidl::android::hardware::graphics::composer3::Luts>*) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -491,6 +494,8 @@ public: int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) override; status_t setDisplayPictureProfileHandle(PhysicalDisplayId, const android::PictureProfileHandle& profile) override; + status_t getLuts(PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&, + std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index 5703a2d37c..ec155392eb 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -1452,6 +1452,11 @@ Error HidlComposer::getMaxLayerPictureProfiles(Display, int32_t*) { return Error::UNSUPPORTED; } +Error HidlComposer::getLuts(Display, const std::vector<sp<GraphicBuffer>>&, + std::vector<aidl::android::hardware::graphics::composer3::Luts>*) { + return Error::UNSUPPORTED; +} + Error HidlComposer::setDisplayPictureProfileId(Display, PictureProfileId) { return Error::UNSUPPORTED; } diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index 42ba9a957b..cacdb8ce52 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -359,6 +359,8 @@ public: Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override; Error setDisplayPictureProfileId(Display, PictureProfileId) override; Error setLayerPictureProfileId(Display, Layer, PictureProfileId) override; + Error getLuts(Display, const std::vector<sp<GraphicBuffer>>&, + std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index 58f6b96e57..367132c113 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -418,7 +418,7 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate } if (forceUpdate || requested.what & layer_state_t::eAppContentPriorityChanged) { // TODO(b/337330263): Also consider the system-determined priority of the app - pictureProfilePriority = requested.appContentPriority; + pictureProfilePriority = int64_t(requested.appContentPriority) + INT_MAX; } if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) { diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 4d9a9ca06e..022588deef 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -262,7 +262,10 @@ void updateVisibility(LayerSnapshot& snapshot, bool visible) { snapshot.isVisible = visible; if (FlagManager::getInstance().skip_invisible_windows_in_input()) { - snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible); + const bool visibleForInput = + snapshot.isVisible || (snapshot.hasInputInfo() && !snapshot.isHiddenByPolicy()); + snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, + !visibleForInput); } else { // TODO(b/238781169) we are ignoring this compat for now, since we will have // to remove any optimization based on visibility. diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 21d3396ebe..d3483b0cb0 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -346,7 +346,6 @@ void RegionSamplingThread::captureSample() { constexpr bool kRegionSampling = true; constexpr bool kGrayscale = false; constexpr bool kIsProtected = false; - constexpr bool kAttachGainmap = false; SurfaceFlinger::RenderAreaBuilderVariant renderAreaBuilder(std::in_place_type<DisplayRenderAreaBuilder>, sampledBounds, @@ -358,7 +357,7 @@ void RegionSamplingThread::captureSample() { mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers); FenceResult fenceResult = mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale, - kIsProtected, kAttachGainmap, nullptr, displayState, layers) + kIsProtected, nullptr, displayState, layers) .get(); if (fenceResult.ok()) { fenceResult.value()->waitForever(LOG_TAG); diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 6e2b943509..8c22de142d 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -504,7 +504,7 @@ FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compati return FrameRateCompatibility::Exact; case ANATIVEWINDOW_FRAME_RATE_MIN: return FrameRateCompatibility::Min; - case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE: + case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST: return FrameRateCompatibility::Gte; case ANATIVEWINDOW_FRAME_RATE_NO_VOTE: return FrameRateCompatibility::NoVote; diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index a2cdd460ca..3fdddac52a 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -209,7 +209,6 @@ public: ftl::FakeGuard guard(kMainThreadContext); resyncToHardwareVsyncLocked(id, allowToEnable, modePtr); } - void resync() override EXCLUDES(mDisplayLock); void forceNextResync() { mLastResyncTime = 0; } // Passes a vsync sample to VsyncController. Returns true if @@ -471,6 +470,7 @@ private: bool throttleVsync(TimePoint, uid_t) override; // Get frame interval Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock); + void resync() override EXCLUDES(mDisplayLock); void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock); std::unique_ptr<EventThread> mRenderEventThread; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index e6b6f4efb5..e05c5bd97c 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -37,6 +37,7 @@ #include <android/hardware/configstore/1.1/types.h> #include <android/native_window.h> #include <android/os/IInputFlinger.h> +#include <android_os.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionCache.h> @@ -126,7 +127,6 @@ #include <gui/SchedulingPolicy.h> #include <gui/SyncScreenCaptureListener.h> #include <ui/DisplayIdentification.h> -#include "ActivePictureUpdater.h" #include "BackgroundExecutor.h" #include "Client.h" #include "ClientCache.h" @@ -742,7 +742,10 @@ void SurfaceFlinger::bootFinished() { mBootFinished = true; FlagManager::getMutableInstance().markBootCompleted(); - ::tracing_perfetto::registerWithPerfetto(); + if (android::os::perfetto_sdk_tracing()) { + ::tracing_perfetto::registerWithPerfetto(); + } + mInitBootPropsFuture.wait(); mRenderEnginePrimeCacheFuture.wait(); @@ -1444,14 +1447,14 @@ status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToke return future.get(); } -void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) { +bool SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) { SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); const auto pendingModeOpt = mDisplayModeController.getPendingMode(displayId); if (!pendingModeOpt) { // There is no pending mode change. This can happen if the active // display changed and the mode change happened on a different display. - return; + return true; } const auto& activeMode = pendingModeOpt->mode; @@ -1466,8 +1469,8 @@ void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) { state.physical->activeMode = activeMode.modePtr.get(); processDisplayChangesLocked(); - // processDisplayChangesLocked will update all necessary components so we're done here. - return; + // The DisplayDevice has been destroyed, so abort the commit for the now dead FrameTargeter. + return false; } mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(), @@ -1478,6 +1481,8 @@ void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) { if (pendingModeOpt->emitEvent) { mScheduler->onDisplayModeChanged(displayId, activeMode, /*clearContentRequirements*/ true); } + + return true; } void SurfaceFlinger::dropModeRequest(PhysicalDisplayId displayId) { @@ -2659,7 +2664,10 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, for (const auto [displayId, _] : frameTargets) { if (mDisplayModeController.isModeSetPending(displayId)) { - finalizeDisplayModeChange(displayId); + if (!finalizeDisplayModeChange(displayId)) { + mScheduler->scheduleFrame(); + return false; + } } } } @@ -2788,12 +2796,43 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays); refreshArgs.outputs.reserve(displays.size()); + // Track layer stacks of physical displays that might be added to CompositionEngine + // output. Layer stacks are not tracked in Display when we iterate through + // frameTargeters. Cross-referencing layer stacks allows us to filter out displays + // by ID with duplicate layer stacks before adding them to CompositionEngine output. + ui::DisplayMap<DisplayId, ui::LayerStack> physicalDisplayLayerStacks; + for (auto& [_, display] : displays) { + const auto id = PhysicalDisplayId::tryCast(display->getId()); + if (id && frameTargeters.contains(*id)) { + physicalDisplayLayerStacks.try_emplace(*id, display->getLayerStack()); + } + } + + // Tracks layer stacks of displays that are added to CompositionEngine output. + ui::DisplayMap<ui::LayerStack, ftl::Unit> outputLayerStacks; + auto isOutputLayerStack = [&outputLayerStacks](DisplayId id, ui::LayerStack layerStack) { + if (FlagManager::getInstance().reject_dupe_layerstacks() && + outputLayerStacks.contains(layerStack)) { + // TODO: remove log and DisplayId from params once reject_dupe_layerstacks flag is + // removed + ALOGD("Existing layer stack ID %d output to another display %" PRIu64 + ", dropping display from outputs", + layerStack.id, id.value); + return true; + } + outputLayerStacks.try_emplace(layerStack); + return false; + }; + // Add outputs for physical displays. for (const auto& [id, targeter] : frameTargeters) { ftl::FakeGuard guard(mStateLock); if (const auto display = getCompositionDisplayLocked(id)) { - refreshArgs.outputs.push_back(display); + const auto layerStack = physicalDisplayLayerStacks.get(id)->get(); + if (!isOutputLayerStack(display->getId(), layerStack)) { + refreshArgs.outputs.push_back(display); + } } refreshArgs.frameTargets.try_emplace(id, &targeter->target()); @@ -2810,7 +2849,9 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( if (!refreshRate.isValid() || mScheduler->isVsyncInPhase(pacesetterTarget.frameBeginTime(), refreshRate)) { - refreshArgs.outputs.push_back(display->getCompositionDisplay()); + if (!isOutputLayerStack(display->getId(), display->getLayerStack())) { + refreshArgs.outputs.push_back(display->getCompositionDisplay()); + } } } } @@ -2914,7 +2955,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( layer->setWasClientComposed(compositionResult.lastClientCompositionFence); } if (com_android_graphics_libgui_flags_apply_picture_profiles()) { - mActivePictureUpdater.onLayerComposed(*layer, *layerFE, compositionResult); + mActivePictureTracker.onLayerComposed(*layer, *layerFE, compositionResult); } } @@ -3226,8 +3267,8 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>> hdrInfoListeners; bool haveNewHdrInfoListeners = false; - sp<gui::IActivePictureListener> activePictureListener; - bool haveNewActivePictureListener = false; + ActivePictureTracker::Listeners activePictureListenersToAdd; + ActivePictureTracker::Listeners activePictureListenersToRemove; { Mutex::Autolock lock(mStateLock); if (mFpsReporter) { @@ -3249,9 +3290,8 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, haveNewHdrInfoListeners = mAddingHDRLayerInfoListener; // grab this with state lock mAddingHDRLayerInfoListener = false; - activePictureListener = mActivePictureListener; - haveNewActivePictureListener = mHaveNewActivePictureListener; - mHaveNewActivePictureListener = false; + std::swap(activePictureListenersToAdd, mActivePictureListenersToAdd); + std::swap(activePictureListenersToRemove, mActivePictureListenersToRemove); } if (haveNewHdrInfoListeners || mHdrLayerInfoChanged) { @@ -3315,14 +3355,10 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, mHdrLayerInfoChanged = false; if (com_android_graphics_libgui_flags_apply_picture_profiles()) { - // Track, update and notify changes to active pictures - layers that are undergoing picture - // processing - if (mActivePictureUpdater.updateAndHasChanged() || haveNewActivePictureListener) { - if (activePictureListener) { - activePictureListener->onActivePicturesChanged( - mActivePictureUpdater.getActivePictures()); - } - } + // Track, update and notify changes to active pictures - layers that are undergoing + // picture processing + mActivePictureTracker.updateAndNotifyListeners(activePictureListenersToAdd, + activePictureListenersToRemove); } mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */); @@ -4569,7 +4605,6 @@ void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule sche SFTRACE_INT("mTransactionFlags", transactionFlags); if (const bool scheduled = transactionFlags & mask; !scheduled) { - mScheduler->resync(); scheduleCommit(frameHint); } else if (frameHint == FrameHint::kActive) { // Even if the next frame is already scheduled, we should reset the idle timer @@ -7152,8 +7187,7 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, displayWeak, options), getLayerSnapshotsFn, reqSize, static_cast<ui::PixelFormat>(captureArgs.pixelFormat), - captureArgs.allowProtected, captureArgs.grayscale, - captureArgs.attachGainmap, captureListener); + captureArgs.allowProtected, captureArgs.grayscale, captureListener); } void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args, @@ -7210,7 +7244,7 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args static_cast<ui::Dataspace>(args.dataspace), displayWeak, options), getLayerSnapshotsFn, size, static_cast<ui::PixelFormat>(args.pixelFormat), - kAllowProtected, kGrayscale, args.attachGainmap, captureListener); + kAllowProtected, kGrayscale, captureListener); } ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) { @@ -7321,8 +7355,7 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, options), getLayerSnapshotsFn, reqSize, static_cast<ui::PixelFormat>(captureArgs.pixelFormat), - captureArgs.allowProtected, captureArgs.grayscale, - captureArgs.attachGainmap, captureListener); + captureArgs.allowProtected, captureArgs.grayscale, captureListener); } // Creates a Future release fence for a layer and keeps track of it in a list to @@ -7380,7 +7413,7 @@ std::optional<SurfaceFlinger::OutputCompositionState> SurfaceFlinger::getSnapsho void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, ui::Size bufferSize, ui::PixelFormat reqPixelFormat, - bool allowProtected, bool grayscale, bool attachGainmap, + bool allowProtected, bool grayscale, const sp<IScreenCaptureListener>& captureListener) { SFTRACE_CALL(); @@ -7395,6 +7428,10 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuil std::vector<std::pair<Layer*, sp<LayerFE>>> layers; auto displayState = getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers); + const bool hasHdrLayer = std::any_of(layers.cbegin(), layers.cend(), [this](const auto& layer) { + return isHdrLayer(*(layer.second->mSnapshot.get())); + }); + const bool supportsProtected = getRenderEngine().supportsProtectedContent(); bool hasProtectedLayer = false; if (allowProtected && supportsProtected) { @@ -7423,9 +7460,51 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuil renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), renderengine::impl::ExternalTexture::Usage:: WRITEABLE); - auto futureFence = - captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale, - isProtected, attachGainmap, captureListener, displayState, layers); + + std::shared_ptr<renderengine::impl::ExternalTexture> hdrTexture; + std::shared_ptr<renderengine::impl::ExternalTexture> gainmapTexture; + + bool hintForSeamless = std::visit( + [](auto&& arg) { + return arg.options.test(RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION); + }, + renderAreaBuilder); + if (hasHdrLayer && !hintForSeamless && FlagManager::getInstance().true_hdr_screenshots()) { + const auto hdrBuffer = + getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(), + HAL_PIXEL_FORMAT_RGBA_FP16, 1 /* layerCount */, + buffer->getUsage(), "screenshot-hdr"); + const auto gainmapBuffer = + getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(), + buffer->getPixelFormat(), 1 /* layerCount */, + buffer->getUsage(), "screenshot-gainmap"); + + const status_t hdrBufferStatus = hdrBuffer->initCheck(); + const status_t gainmapBufferStatus = gainmapBuffer->initCheck(); + + if (hdrBufferStatus != OK || gainmapBufferStatus != -OK) { + if (hdrBufferStatus != OK) { + ALOGW("%s: Buffer failed to allocate for hdr: %d. Screenshoting SDR instead.", + __func__, hdrBufferStatus); + } else { + ALOGW("%s: Buffer failed to allocate for gainmap: %d. Screenshoting SDR instead.", + __func__, gainmapBufferStatus); + } + } else { + hdrTexture = std::make_shared< + renderengine::impl::ExternalTexture>(hdrBuffer, getRenderEngine(), + renderengine::impl::ExternalTexture:: + Usage::WRITEABLE); + gainmapTexture = std::make_shared< + renderengine::impl::ExternalTexture>(gainmapBuffer, getRenderEngine(), + renderengine::impl::ExternalTexture:: + Usage::WRITEABLE); + } + } + + auto futureFence = captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, + grayscale, isProtected, captureListener, displayState, + layers, hdrTexture, gainmapTexture); futureFence.get(); } @@ -7468,10 +7547,11 @@ SurfaceFlinger::getDisplayStateFromRenderAreaBuilder(RenderAreaBuilderVariant& r ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( const RenderAreaBuilderVariant& renderAreaBuilder, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, bool attachGainmap, - const sp<IScreenCaptureListener>& captureListener, - std::optional<OutputCompositionState>& displayState, - std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) { + bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener, + const std::optional<OutputCompositionState>& displayState, + const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, + const std::shared_ptr<renderengine::ExternalTexture>& hdrBuffer, + const std::shared_ptr<renderengine::ExternalTexture>& gainmapBuffer) { SFTRACE_CALL(); ScreenCaptureResults captureResults; @@ -7487,74 +7567,40 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( } return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share(); } + float displayBrightnessNits = displayState.value().displayBrightnessNits; float sdrWhitePointNits = displayState.value().sdrWhitePointNits; - ftl::SharedFuture<FenceResult> renderFuture = - renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected, - captureResults, displayState, layers); - - if (captureResults.capturedHdrLayers && attachGainmap && - FlagManager::getInstance().true_hdr_screenshots()) { - sp<GraphicBuffer> hdrBuffer = - getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(), - HAL_PIXEL_FORMAT_RGBA_FP16, 1 /* layerCount */, - buffer->getUsage(), "screenshot-hdr"); - sp<GraphicBuffer> gainmapBuffer = - getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(), - buffer->getPixelFormat(), 1 /* layerCount */, - buffer->getUsage(), "screenshot-gainmap"); - - const status_t bufferStatus = hdrBuffer->initCheck(); - const status_t gainmapBufferStatus = gainmapBuffer->initCheck(); - - if (bufferStatus != OK) { - ALOGW("%s: Buffer failed to allocate for hdr: %d. Screenshoting SDR instead.", __func__, - bufferStatus); - } else if (gainmapBufferStatus != OK) { - ALOGW("%s: Buffer failed to allocate for gainmap: %d. Screenshoting SDR instead.", - __func__, gainmapBufferStatus); - } else { - captureResults.optionalGainMap = gainmapBuffer; - const auto hdrTexture = std::make_shared< - renderengine::impl::ExternalTexture>(hdrBuffer, getRenderEngine(), - renderengine::impl::ExternalTexture:: - Usage::WRITEABLE); - const auto gainmapTexture = std::make_shared< - renderengine::impl::ExternalTexture>(gainmapBuffer, getRenderEngine(), - renderengine::impl::ExternalTexture:: - Usage::WRITEABLE); - ScreenCaptureResults unusedResults; - ftl::SharedFuture<FenceResult> hdrRenderFuture = - renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale, - isProtected, unusedResults, displayState, layers); - - renderFuture = - ftl::Future(std::move(renderFuture)) - .then([&, hdrRenderFuture = std::move(hdrRenderFuture), - displayBrightnessNits, sdrWhitePointNits, - dataspace = captureResults.capturedDataspace, buffer, hdrTexture, - gainmapTexture](FenceResult fenceResult) -> FenceResult { - if (!fenceResult.ok()) { - return fenceResult; - } - - auto hdrFenceResult = hdrRenderFuture.get(); - - if (!hdrFenceResult.ok()) { - return hdrFenceResult; - } + ftl::SharedFuture<FenceResult> renderFuture; + + if (hdrBuffer && gainmapBuffer) { + ftl::SharedFuture<FenceResult> hdrRenderFuture = + renderScreenImpl(renderArea.get(), hdrBuffer, regionSampling, grayscale, + isProtected, captureResults, displayState, layers); + captureResults.buffer = buffer->getBuffer(); + captureResults.optionalGainMap = gainmapBuffer->getBuffer(); + + renderFuture = + ftl::Future(std::move(hdrRenderFuture)) + .then([&, displayBrightnessNits, sdrWhitePointNits, + dataspace = captureResults.capturedDataspace, buffer, hdrBuffer, + gainmapBuffer](FenceResult fenceResult) -> FenceResult { + if (!fenceResult.ok()) { + return fenceResult; + } - return getRenderEngine() - .drawGainmap(buffer, fenceResult.value()->get(), hdrTexture, - hdrFenceResult.value()->get(), - displayBrightnessNits / sdrWhitePointNits, - static_cast<ui::Dataspace>(dataspace), - gainmapTexture) - .get(); - }) - .share(); - }; + return getRenderEngine() + .tonemapAndDrawGainmap(hdrBuffer, fenceResult.value()->get(), + displayBrightnessNits / + sdrWhitePointNits, + static_cast<ui::Dataspace>(dataspace), + buffer, gainmapBuffer) + .get(); + }) + .share(); + } else { + renderFuture = renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, + isProtected, captureResults, displayState, layers); } if (captureListener) { @@ -7576,8 +7622,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults, - std::optional<OutputCompositionState>& displayState, - std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) { + const std::optional<OutputCompositionState>& displayState, + const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) { SFTRACE_CALL(); for (auto& [_, layerFE] : layers) { @@ -7645,9 +7691,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( } auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace, - sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected, - layers = std::move(layers), layerStack, regionSampling, - renderArea = std::move(renderArea), renderIntent, + sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected, layers, + layerStack, regionSampling, renderArea = std::move(renderArea), renderIntent, enableLocalTonemapping]() -> FenceResult { std::unique_ptr<compositionengine::CompositionEngine> compositionEngine = mFactory.createCompositionEngine(); @@ -8178,12 +8223,20 @@ void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t con })); } -void SurfaceFlinger::setActivePictureListener(const sp<gui::IActivePictureListener>& listener) { - if (com_android_graphics_libgui_flags_apply_picture_profiles()) { - Mutex::Autolock lock(mStateLock); - mActivePictureListener = listener; - mHaveNewActivePictureListener = listener != nullptr; - } +void SurfaceFlinger::addActivePictureListener(const sp<gui::IActivePictureListener>& listener) { + Mutex::Autolock lock(mStateLock); + std::erase_if(mActivePictureListenersToRemove, [listener](const auto& otherListener) { + return IInterface::asBinder(listener) == IInterface::asBinder(otherListener); + }); + mActivePictureListenersToAdd.push_back(listener); +} + +void SurfaceFlinger::removeActivePictureListener(const sp<gui::IActivePictureListener>& listener) { + Mutex::Autolock lock(mStateLock); + std::erase_if(mActivePictureListenersToAdd, [listener](const auto& otherListener) { + return IInterface::asBinder(listener) == IInterface::asBinder(otherListener); + }); + mActivePictureListenersToRemove.push_back(listener); } std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData( @@ -9136,11 +9189,20 @@ binder::Status SurfaceComposerAIDL::removeHdrLayerInfoListener( return binderStatusFromStatusT(status); } -binder::Status SurfaceComposerAIDL::setActivePictureListener( +binder::Status SurfaceComposerAIDL::addActivePictureListener( + const sp<gui::IActivePictureListener>& listener) { + status_t status = checkObservePictureProfilesPermission(); + if (status == OK) { + mFlinger->addActivePictureListener(listener); + } + return binderStatusFromStatusT(status); +} + +binder::Status SurfaceComposerAIDL::removeActivePictureListener( const sp<gui::IActivePictureListener>& listener) { status_t status = checkObservePictureProfilesPermission(); if (status == OK) { - mFlinger->setActivePictureListener(listener); + mFlinger->removeActivePictureListener(listener); } return binderStatusFromStatusT(status); } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 1e2c08747b..c85c0846c5 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -69,7 +69,7 @@ #include <ui/FenceResult.h> #include <common/FlagManager.h> -#include "ActivePictureUpdater.h" +#include "ActivePictureTracker.h" #include "BackgroundExecutor.h" #include "Display/DisplayModeController.h" #include "Display/PhysicalDisplay.h" @@ -667,7 +667,9 @@ private: void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel); - void setActivePictureListener(const sp<gui::IActivePictureListener>& listener); + void addActivePictureListener(const sp<gui::IActivePictureListener>& listener); + + void removeActivePictureListener(const sp<gui::IActivePictureListener>& listener); // IBinder::DeathRecipient overrides: void binderDied(const wp<IBinder>& who) override; @@ -730,7 +732,11 @@ private: Fps maxFps); void initiateDisplayModeChanges() REQUIRES(kMainThreadContext) REQUIRES(mStateLock); - void finalizeDisplayModeChange(PhysicalDisplayId) REQUIRES(kMainThreadContext) + + // Returns whether the commit stage should proceed. The return value is ignored when finalizing + // immediate mode changes, which happen toward the end of the commit stage. + // TODO: b/355427258 - Remove the return value once the `synced_resolution_switch` flag is live. + bool finalizeDisplayModeChange(PhysicalDisplayId) REQUIRES(kMainThreadContext) REQUIRES(mStateLock); void dropModeRequest(PhysicalDisplayId) REQUIRES(kMainThreadContext); @@ -872,7 +878,7 @@ private: void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction, ui::Size bufferSize, ui::PixelFormat, bool allowProtected, - bool grayscale, bool attachGainmap, const sp<IScreenCaptureListener>&); + bool grayscale, const sp<IScreenCaptureListener>&); std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder( RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext); @@ -880,16 +886,17 @@ private: ftl::SharedFuture<FenceResult> captureScreenshot( const RenderAreaBuilderVariant& renderAreaBuilder, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, bool attachGainmap, - const sp<IScreenCaptureListener>& captureListener, - std::optional<OutputCompositionState>& displayState, - std::vector<std::pair<Layer*, sp<LayerFE>>>& layers); + bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener, + const std::optional<OutputCompositionState>& displayState, + const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, + const std::shared_ptr<renderengine::ExternalTexture>& hdrBuffer = nullptr, + const std::shared_ptr<renderengine::ExternalTexture>& gainmapBuffer = nullptr); ftl::SharedFuture<FenceResult> renderScreenImpl( const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&, - std::optional<OutputCompositionState>& displayState, - std::vector<std::pair<Layer*, sp<LayerFE>>>& layers); + const std::optional<OutputCompositionState>& displayState, + const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers); void readPersistentProperties(); @@ -1402,9 +1409,9 @@ private: std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners GUARDED_BY(mStateLock); - sp<gui::IActivePictureListener> mActivePictureListener GUARDED_BY(mStateLock); - bool mHaveNewActivePictureListener GUARDED_BY(mStateLock); - ActivePictureUpdater mActivePictureUpdater GUARDED_BY(kMainThreadContext); + ActivePictureTracker mActivePictureTracker GUARDED_BY(kMainThreadContext); + ActivePictureTracker::Listeners mActivePictureListenersToAdd GUARDED_BY(mStateLock); + ActivePictureTracker::Listeners mActivePictureListenersToRemove GUARDED_BY(mStateLock); std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint; @@ -1641,8 +1648,8 @@ public: binder::Status flushJankData(int32_t layerId) override; binder::Status removeJankListener(int32_t layerId, const sp<gui::IJankListener>& listener, int64_t afterVsync) override; - binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>& listener); - binder::Status clearActivePictureListener(); + binder::Status addActivePictureListener(const sp<gui::IActivePictureListener>& listener); + binder::Status removeActivePictureListener(const sp<gui::IActivePictureListener>& listener); private: static const constexpr bool kUsePermissionCache = true; diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 5e78426c77..e80cd78cbd 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -112,60 +112,63 @@ void FlagManager::dump(std::string& result) const { DUMP_LEGACY_SERVER_FLAG(use_skia_tracing); /// Trunk stable server (R/W) flags /// - DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display); DUMP_ACONFIG_FLAG(adpf_gpu_sf); DUMP_ACONFIG_FLAG(adpf_native_session_manager); DUMP_ACONFIG_FLAG(adpf_use_fmq_channel); DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout); + DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display); /// Trunk stable readonly flags /// + /// IMPORTANT - please keep alphabetize to reduce merge conflicts + DUMP_ACONFIG_FLAG(add_sf_skipped_frames_to_trace); DUMP_ACONFIG_FLAG(adpf_fmq_sf); + DUMP_ACONFIG_FLAG(allow_n_vsyncs_in_targeter); DUMP_ACONFIG_FLAG(arr_setframerate_gte_enum); - DUMP_ACONFIG_FLAG(connected_display); - DUMP_ACONFIG_FLAG(enable_small_area_detection); - DUMP_ACONFIG_FLAG(stable_edid_ids); - DUMP_ACONFIG_FLAG(frame_rate_category_mrr); - DUMP_ACONFIG_FLAG(misc1); - DUMP_ACONFIG_FLAG(vrr_config); - DUMP_ACONFIG_FLAG(hdcp_level_hal); - DUMP_ACONFIG_FLAG(multithreaded_present); - DUMP_ACONFIG_FLAG(add_sf_skipped_frames_to_trace); - DUMP_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency); + DUMP_ACONFIG_FLAG(begone_bright_hlg); DUMP_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved); - DUMP_ACONFIG_FLAG(enable_fro_dependent_features); + DUMP_ACONFIG_FLAG(commit_not_composited); + DUMP_ACONFIG_FLAG(connected_display); + DUMP_ACONFIG_FLAG(connected_display_hdr); + DUMP_ACONFIG_FLAG(correct_dpi_with_display_size); + DUMP_ACONFIG_FLAG(deprecate_frame_tracker); + DUMP_ACONFIG_FLAG(deprecate_vsync_sf); + DUMP_ACONFIG_FLAG(detached_mirror); + DUMP_ACONFIG_FLAG(display_config_error_hal); DUMP_ACONFIG_FLAG(display_protected); + DUMP_ACONFIG_FLAG(dont_skip_on_early_ro); + DUMP_ACONFIG_FLAG(enable_fro_dependent_features); + DUMP_ACONFIG_FLAG(enable_layer_command_batching); + DUMP_ACONFIG_FLAG(enable_small_area_detection); + DUMP_ACONFIG_FLAG(filter_frames_before_trace_starts); + DUMP_ACONFIG_FLAG(flush_buffer_slots_to_uncache); + DUMP_ACONFIG_FLAG(force_compile_graphite_renderengine); DUMP_ACONFIG_FLAG(fp16_client_target); + DUMP_ACONFIG_FLAG(frame_rate_category_mrr); DUMP_ACONFIG_FLAG(game_default_frame_rate); - DUMP_ACONFIG_FLAG(enable_layer_command_batching); - DUMP_ACONFIG_FLAG(vulkan_renderengine); - DUMP_ACONFIG_FLAG(renderable_buffer_usage); - DUMP_ACONFIG_FLAG(vrr_bugfix_24q4); - DUMP_ACONFIG_FLAG(vrr_bugfix_dropped_frame); - DUMP_ACONFIG_FLAG(restore_blur_step); - DUMP_ACONFIG_FLAG(dont_skip_on_early_ro); - DUMP_ACONFIG_FLAG(no_vsyncs_on_screen_off); - DUMP_ACONFIG_FLAG(protected_if_client); - DUMP_ACONFIG_FLAG(idle_screen_refresh_rate_timeout); DUMP_ACONFIG_FLAG(graphite_renderengine); - DUMP_ACONFIG_FLAG(filter_frames_before_trace_starts); + DUMP_ACONFIG_FLAG(hdcp_level_hal); + DUMP_ACONFIG_FLAG(idle_screen_refresh_rate_timeout); DUMP_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed); - DUMP_ACONFIG_FLAG(deprecate_vsync_sf); - DUMP_ACONFIG_FLAG(allow_n_vsyncs_in_targeter); - DUMP_ACONFIG_FLAG(detached_mirror); - DUMP_ACONFIG_FLAG(commit_not_composited); - DUMP_ACONFIG_FLAG(correct_dpi_with_display_size); DUMP_ACONFIG_FLAG(local_tonemap_screenshots); + DUMP_ACONFIG_FLAG(misc1); + DUMP_ACONFIG_FLAG(multithreaded_present); + DUMP_ACONFIG_FLAG(no_vsyncs_on_screen_off); DUMP_ACONFIG_FLAG(override_trusted_overlay); - DUMP_ACONFIG_FLAG(flush_buffer_slots_to_uncache); - DUMP_ACONFIG_FLAG(force_compile_graphite_renderengine); + DUMP_ACONFIG_FLAG(protected_if_client); + DUMP_ACONFIG_FLAG(reject_dupe_layerstacks); + DUMP_ACONFIG_FLAG(renderable_buffer_usage); + DUMP_ACONFIG_FLAG(restore_blur_step); + DUMP_ACONFIG_FLAG(skip_invisible_windows_in_input); + DUMP_ACONFIG_FLAG(stable_edid_ids); DUMP_ACONFIG_FLAG(trace_frame_rate_override); DUMP_ACONFIG_FLAG(true_hdr_screenshots); - DUMP_ACONFIG_FLAG(display_config_error_hal); - DUMP_ACONFIG_FLAG(connected_display_hdr); - DUMP_ACONFIG_FLAG(deprecate_frame_tracker); - DUMP_ACONFIG_FLAG(skip_invisible_windows_in_input); - DUMP_ACONFIG_FLAG(begone_bright_hlg); + DUMP_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency); + DUMP_ACONFIG_FLAG(vrr_bugfix_24q4); + DUMP_ACONFIG_FLAG(vrr_bugfix_dropped_frame); + DUMP_ACONFIG_FLAG(vrr_config); + DUMP_ACONFIG_FLAG(vulkan_renderengine); DUMP_ACONFIG_FLAG(window_blur_kawase2); + /// IMPORTANT - please keep alphabetize to reduce merge conflicts #undef DUMP_ACONFIG_FLAG #undef DUMP_LEGACY_SERVER_FLAG @@ -266,6 +269,7 @@ FLAG_MANAGER_ACONFIG_FLAG(deprecate_frame_tracker, ""); FLAG_MANAGER_ACONFIG_FLAG(skip_invisible_windows_in_input, ""); FLAG_MANAGER_ACONFIG_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg"); FLAG_MANAGER_ACONFIG_FLAG(window_blur_kawase2, ""); +FLAG_MANAGER_ACONFIG_FLAG(reject_dupe_layerstacks, ""); /// Trunk stable server (R/W) flags /// FLAG_MANAGER_ACONFIG_FLAG(refresh_rate_overlay_on_external_display, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index d8887f538f..c7f97b4008 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -48,62 +48,65 @@ public: bool use_skia_tracing() const; /// Trunk stable server (R/W) flags /// - bool refresh_rate_overlay_on_external_display() const; bool adpf_gpu_sf() const; - bool adpf_use_fmq_channel() const; bool adpf_native_session_manager() const; + bool adpf_use_fmq_channel() const; bool adpf_use_fmq_channel_fixed() const; bool graphite_renderengine_preview_rollout() const; + bool refresh_rate_overlay_on_external_display() const; /// Trunk stable readonly flags /// - bool arr_setframerate_gte_enum() const; - bool adpf_fmq_sf() const; - bool connected_display() const; - bool frame_rate_category_mrr() const; - bool enable_small_area_detection() const; - bool stable_edid_ids() const; - bool misc1() const; - bool vrr_config() const; - bool hdcp_level_hal() const; - bool multithreaded_present() const; + /// IMPORTANT - please keep alphabetize to reduce merge conflicts bool add_sf_skipped_frames_to_trace() const; - bool use_known_refresh_rate_for_fps_consistency() const; + bool adpf_fmq_sf() const; + bool allow_n_vsyncs_in_targeter() const; + bool arr_setframerate_gte_enum() const; + bool begone_bright_hlg() const; bool cache_when_source_crop_layer_only_moved() const; - bool enable_fro_dependent_features() const; + bool commit_not_composited() const; + bool connected_display() const; + bool connected_display_hdr() const; + bool correct_dpi_with_display_size() const; + bool deprecate_frame_tracker() const; + bool deprecate_vsync_sf() const; + bool detached_mirror() const; + bool display_config_error_hal() const; bool display_protected() const; + bool dont_skip_on_early_ro() const; + bool enable_fro_dependent_features() const; + bool enable_layer_command_batching() const; + bool enable_small_area_detection() const; + bool filter_frames_before_trace_starts() const; + bool flush_buffer_slots_to_uncache() const; + bool force_compile_graphite_renderengine() const; bool fp16_client_target() const; + bool frame_rate_category_mrr() const; bool game_default_frame_rate() const; - bool enable_layer_command_batching() const; - bool vulkan_renderengine() const; - bool vrr_bugfix_24q4() const; - bool vrr_bugfix_dropped_frame() const; - bool renderable_buffer_usage() const; - bool restore_blur_step() const; - bool dont_skip_on_early_ro() const; - bool no_vsyncs_on_screen_off() const; - bool protected_if_client() const; - bool idle_screen_refresh_rate_timeout() const; bool graphite_renderengine() const; - bool filter_frames_before_trace_starts() const; + bool hdcp_level_hal() const; + bool idle_screen_refresh_rate_timeout() const; bool latch_unsignaled_with_auto_refresh_changed() const; - bool deprecate_vsync_sf() const; - bool allow_n_vsyncs_in_targeter() const; - bool detached_mirror() const; - bool commit_not_composited() const; - bool correct_dpi_with_display_size() const; bool local_tonemap_screenshots() const; + bool luts_api() const; + bool misc1() const; + bool multithreaded_present() const; + bool no_vsyncs_on_screen_off() const; bool override_trusted_overlay() const; - bool flush_buffer_slots_to_uncache() const; - bool force_compile_graphite_renderengine() const; + bool protected_if_client() const; + bool reject_dupe_layerstacks() const; + bool renderable_buffer_usage() const; + bool restore_blur_step() const; + bool skip_invisible_windows_in_input() const; + bool stable_edid_ids() const; bool trace_frame_rate_override() const; bool true_hdr_screenshots() const; - bool display_config_error_hal() const; - bool connected_display_hdr() const; - bool deprecate_frame_tracker() const; - bool skip_invisible_windows_in_input() const; - bool begone_bright_hlg() const; - bool luts_api() const; + bool use_known_refresh_rate_for_fps_consistency() const; + bool vrr_bugfix_24q4() const; + bool vrr_bugfix_dropped_frame() const; + bool vrr_config() const; + bool vulkan_renderengine() const; bool window_blur_kawase2() const; + /// IMPORTANT - please keep alphabetize to reduce merge conflicts protected: // overridden for unit tests diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index bdd826d084..b28d2697c5 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -217,6 +217,17 @@ flag { } # no_vsyncs_on_screen_off flag { + name: "reject_dupe_layerstacks" + namespace: "window_surfaces" + description: "Reject duplicate layerstacks for displays" + bug: "370358572" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } + } # reject_dupe_layerstacks + +flag { name: "single_hop_screenshot" namespace: "window_surfaces" description: "Only access SF main thread once during a screenshot" diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index 4d5c0fd1de..b5f7a74347 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -52,7 +52,7 @@ cc_test { "LayerTypeTransaction_test.cpp", "LayerUpdate_test.cpp", "MirrorLayer_test.cpp", - "MultiDisplayLayerBounds_test.cpp", + "MultiDisplay_test.cpp", "RefreshRateOverlay_test.cpp", "RelativeZ_test.cpp", "ReleaseBufferCallback_test.cpp", diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplay_test.cpp index 65add63165..54bb253526 100644 --- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp +++ b/services/surfaceflinger/tests/MultiDisplay_test.cpp @@ -33,7 +33,7 @@ using android::hardware::graphics::common::V1_1::BufferUsage; ::testing::Environment* const binderEnv = ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); -class MultiDisplayLayerBoundsTest : public LayerTransactionTest { +class MultiDisplayTest : public LayerTransactionTest { protected: virtual void SetUp() { LayerTransactionTest::SetUp(); @@ -105,7 +105,7 @@ protected: Color mExpectedColor = {63, 63, 195, 255}; }; -TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) { +TEST_F(MultiDisplayTest, RenderLayerInVirtualDisplay) { constexpr ui::LayerStack kLayerStack{1u}; createDisplay(mMainDisplayState.layerStackSpaceRect, kLayerStack); createColorLayer(kLayerStack); @@ -124,7 +124,7 @@ TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) { sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 255}); } -TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) { +TEST_F(MultiDisplayTest, RenderLayerInMirroredVirtualDisplay) { // Create a display and set its layer stack to the main display's layer stack so // the contents of the main display are mirrored on to the virtual display. @@ -150,7 +150,7 @@ TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) { sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); } -TEST_F(MultiDisplayLayerBoundsTest, RenderLayerWithPromisedFenceInMirroredVirtualDisplay) { +TEST_F(MultiDisplayTest, RenderLayerWithPromisedFenceInMirroredVirtualDisplay) { // Create a display and use a unique layerstack ID for mirrorDisplay() so // the contents of the main display are mirrored on to the virtual display. @@ -181,6 +181,51 @@ TEST_F(MultiDisplayLayerBoundsTest, RenderLayerWithPromisedFenceInMirroredVirtua sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) +TEST_F(MultiDisplayTest, rejectDuplicateLayerStacks) { + if (!FlagManager::getInstance().reject_dupe_layerstacks()) return; + + // Setup + sp<CpuConsumer> cpuConsumer1 = sp<CpuConsumer>::make(static_cast<size_t>(1)); + cpuConsumer1->setName(String8("consumer 1")); + cpuConsumer1->setDefaultBufferSize(100, 100); + sp<IGraphicBufferProducer> cpuProducer1 = + cpuConsumer1->getSurface()->getIGraphicBufferProducer(); + CpuConsumer::LockedBuffer buffer1; + + sp<CpuConsumer> cpuConsumer2 = sp<CpuConsumer>::make(static_cast<size_t>(1)); + cpuConsumer2->setName(String8("consumer 2")); + cpuConsumer2->setDefaultBufferSize(100, 100); + sp<IGraphicBufferProducer> cpuProducer2 = + cpuConsumer2->getSurface()->getIGraphicBufferProducer(); + CpuConsumer::LockedBuffer buffer2; + + SurfaceComposerClient::Transaction t; + constexpr ui::LayerStack layerStack = {123u}; + createColorLayer(layerStack); + + static const std::string kDisplayName1("VirtualDisplay1 - rejectDuplicateLayerStacks"); + sp<IBinder> virtualDisplay1 = + SurfaceComposerClient::createVirtualDisplay(kDisplayName1, false /*isSecure*/); + + t.setDisplaySurface(virtualDisplay1, cpuProducer1); + t.setDisplayLayerStack(virtualDisplay1, layerStack); + t.apply(true); + + static const std::string kDisplayName2("VirtualDisplay2 - rejectDuplicateLayerStacks"); + sp<IBinder> virtualDisplay2 = + SurfaceComposerClient::createVirtualDisplay(kDisplayName2, false /*isSecure*/); + + t.setDisplaySurface(virtualDisplay2, cpuProducer2); + t.setDisplayLayerStack(virtualDisplay2, layerStack); + t.apply(true); + + // The second consumer will not be able to lock a buffer because + // the duplicate layer stack should be rejected. + ASSERT_EQ(NO_ERROR, cpuConsumer1->lockNextBuffer(&buffer1)); + ASSERT_NE(NO_ERROR, cpuConsumer2->lockNextBuffer(&buffer2)); +} +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp new file mode 100644 index 0000000000..8011309519 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/ActivePictureTrackerTest.cpp @@ -0,0 +1,482 @@ +/* + * Copyright 2024 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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <android/gui/ActivePicture.h> +#include <android/gui/IActivePictureListener.h> +#include <compositionengine/mock/CompositionEngine.h> +#include <mock/DisplayHardware/MockComposer.h> +#include <mock/MockLayer.h> +#include <renderengine/mock/RenderEngine.h> + +#include "ActivePictureTracker.h" +#include "LayerFE.h" +#include "TestableSurfaceFlinger.h" + +namespace android { + +using android::compositionengine::LayerFECompositionState; +using android::gui::ActivePicture; +using android::gui::IActivePictureListener; +using android::mock::MockLayer; +using surfaceflinger::frontend::LayerSnapshot; +using testing::_; +using testing::NiceMock; +using testing::Return; +using testing::SizeIs; +using testing::StrictMock; + +class TestableLayerFE : public LayerFE { +public: + TestableLayerFE() : LayerFE("TestableLayerFE"), snapshot(*(new LayerSnapshot)) { + mSnapshot = std::unique_ptr<LayerSnapshot>(&snapshot); + } + + LayerSnapshot& snapshot; +}; + +class MockActivePictureListener : public gui::BnActivePictureListener { +public: + operator ActivePictureTracker::Listeners const() { + return {sp<IActivePictureListener>::fromExisting(this)}; + } + + MOCK_METHOD(binder::Status, onActivePicturesChanged, (const std::vector<ActivePicture>&), + (override)); +}; + +class ActivePictureTrackerTest : public testing::Test { +protected: + const static ActivePictureTracker::Listeners NO_LISTENERS; + + SurfaceFlinger* flinger() { + if (!mFlingerSetup) { + mFlinger.setupMockScheduler(); + mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); + mFlinger.setupRenderEngine(std::make_unique<renderengine::mock::RenderEngine>()); + mFlingerSetup = true; + } + return mFlinger.flinger(); + } + + sp<NiceMock<MockLayer>> createMockLayer(int layerId, int ownerUid) { + auto layer = sp<NiceMock<MockLayer>>::make(flinger(), layerId); + EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(ownerUid))); + return layer; + } + + sp<StrictMock<MockActivePictureListener>> createMockListener() { + return sp<StrictMock<MockActivePictureListener>>::make(); + } + + ActivePictureTracker::Listeners mListenersToAdd; + ActivePictureTracker::Listeners mListenersToRemove; + +private: + TestableSurfaceFlinger mFlinger; + bool mFlingerSetup = false; +}; + +const ActivePictureTracker::Listeners ActivePictureTrackerTest::NO_LISTENERS; + +// Hack to workaround initializer lists not working for parcelables because parcelables inherit from +// Parcelable, which has a virtual destructor. +auto UnorderedElementsAre(std::initializer_list<std::tuple<int32_t, int32_t, int64_t>> tuples) { + std::vector<ActivePicture> activePictures; + for (auto tuple : tuples) { + ActivePicture ap; + ap.layerId = std::get<0>(tuple); + ap.ownerUid = std::get<1>(tuple); + ap.pictureProfileId = std::get<2>(tuple); + activePictures.push_back(ap); + } + return testing::UnorderedElementsAreArray(activePictures); +} + +// Parcelables don't define this for matchers, which is unfortunate +void PrintTo(const ActivePicture& activePicture, std::ostream* os) { + *os << activePicture.toString(); +} + +TEST_F(ActivePictureTrackerTest, whenListenerAdded_called) { + ActivePictureTracker tracker; + auto listener = createMockListener(); + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); +} + +TEST_F(ActivePictureTrackerTest, whenListenerAdded_withListenerAlreadyAdded_notCalled) { + ActivePictureTracker tracker; + auto listener = createMockListener(); + { + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } + { + EXPECT_CALL(*listener, onActivePicturesChanged(_)).Times(0); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenListenerAdded_withUncommittedProfile_calledWithNone) { + auto layer = createMockLayer(100, 10); + TestableLayerFE layerFE; + + ActivePictureTracker tracker; + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS); + } + { + auto listener = createMockListener(); + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenListenerAdded_withCommittedProfile_calledWithActivePicture) { + auto layer = createMockLayer(100, 10); + TestableLayerFE layerFE; + + ActivePictureTracker tracker; + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + auto listener = createMockListener(); + EXPECT_CALL(*listener, onActivePicturesChanged(_)) + .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) { + EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}})); + return binder::Status::ok(); + }); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenProfileAdded_calledWithActivePicture) { + auto layer = createMockLayer(100, 10); + TestableLayerFE layerFE; + + ActivePictureTracker tracker; + auto listener = createMockListener(); + { + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(_)) + .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) { + EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}})); + return binder::Status::ok(); + }); + tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenContinuesUsingProfile_notCalled) { + auto layer = createMockLayer(100, 10); + TestableLayerFE layerFE; + + ActivePictureTracker tracker; + auto listener = createMockListener(); + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(_)).Times(0); + tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenProfileIsRemoved_calledWithNoActivePictures) { + auto layer = createMockLayer(100, 10); + TestableLayerFE layerFE; + + ActivePictureTracker tracker; + auto listener = createMockListener(); + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE; + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1); + tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenProfileIsNotCommitted_calledWithNoActivePictures) { + auto layer = createMockLayer(100, 10); + TestableLayerFE layerFE; + + ActivePictureTracker tracker; + auto listener = createMockListener(); + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(0))).Times(1); + tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenProfileChanges_calledWithDifferentProfile) { + auto layer = createMockLayer(100, 10); + TestableLayerFE layerFE; + + ActivePictureTracker tracker; + auto listener = createMockListener(); + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))) + .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) { + EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}})); + return binder::Status::ok(); + }); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } + { + layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2); + layerFE.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))) + .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) { + EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}})); + return binder::Status::ok(); + }); + tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenMultipleCommittedProfiles_calledWithMultipleActivePictures) { + auto layer1 = createMockLayer(100, 10); + TestableLayerFE layerFE1; + + auto layer2 = createMockLayer(200, 20); + TestableLayerFE layerFE2; + + ActivePictureTracker tracker; + auto listener = createMockListener(); + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2); + layerFE2.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(2))) + .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) { + EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}})); + return binder::Status::ok(); + }); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenNonCommittedProfileChanges_notCalled) { + auto layer1 = createMockLayer(100, 10); + TestableLayerFE layerFE1; + + auto layer2 = createMockLayer(200, 20); + TestableLayerFE layerFE2; + + ActivePictureTracker tracker; + auto listener = createMockListener(); + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); + tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2); + tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(_)).Times(0); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenDifferentLayerUsesSameProfile_called) { + auto layer1 = createMockLayer(100, 10); + TestableLayerFE layerFE1; + + auto layer2 = createMockLayer(200, 20); + TestableLayerFE layerFE2; + + ActivePictureTracker tracker; + auto listener = createMockListener(); + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2); + layerFE2.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(_)) + .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) { + EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}})); + return binder::Status::ok(); + }); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2); + layerFE1.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE2.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(_)) + .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) { + EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}})); + return binder::Status::ok(); + }); + tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenSameUidDifferentLayerUsesSameProfile_called) { + auto layer1 = createMockLayer(100, 10); + TestableLayerFE layerFE1; + + auto layer2 = createMockLayer(200, 10); + TestableLayerFE layerFE2; + + ActivePictureTracker tracker; + auto listener = createMockListener(); + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2); + layerFE2.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(_)) + .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) { + EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}})); + return binder::Status::ok(); + }); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2); + layerFE1.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE2.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(_)) + .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) { + EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}})); + return binder::Status::ok(); + }); + tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS); + } +} + +TEST_F(ActivePictureTrackerTest, whenNewLayerUsesSameProfile_called) { + auto layer1 = createMockLayer(100, 10); + TestableLayerFE layerFE1; + + ActivePictureTracker tracker; + auto listener = createMockListener(); + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(SizeIs(1))).Times(1); + tracker.updateAndNotifyListeners(*listener, NO_LISTENERS); + } + + auto layer2 = createMockLayer(200, 10); + TestableLayerFE layerFE2; + { + layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE1.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); + + layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); + layerFE2.onPictureProfileCommitted(); + tracker.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); + + EXPECT_CALL(*listener, onActivePicturesChanged(_)) + .WillOnce([](const std::vector<gui::ActivePicture>& activePictures) { + EXPECT_THAT(activePictures, UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}})); + return binder::Status::ok(); + }); + tracker.updateAndNotifyListeners(NO_LISTENERS, NO_LISTENERS); + } +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp deleted file mode 100644 index b926d2f4da..0000000000 --- a/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright 2024 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 <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <android/gui/ActivePicture.h> -#include <android/gui/IActivePictureListener.h> -#include <compositionengine/mock/CompositionEngine.h> -#include <mock/DisplayHardware/MockComposer.h> -#include <mock/MockLayer.h> -#include <renderengine/mock/RenderEngine.h> - -#include "ActivePictureUpdater.h" -#include "LayerFE.h" -#include "TestableSurfaceFlinger.h" - -namespace android { - -using android::compositionengine::LayerFECompositionState; -using android::gui::ActivePicture; -using android::gui::IActivePictureListener; -using android::mock::MockLayer; -using surfaceflinger::frontend::LayerSnapshot; -using testing::_; -using testing::NiceMock; -using testing::Return; - -class TestableLayerFE : public LayerFE { -public: - TestableLayerFE() : LayerFE("TestableLayerFE"), snapshot(*(new LayerSnapshot)) { - mSnapshot = std::unique_ptr<LayerSnapshot>(&snapshot); - } - - LayerSnapshot& snapshot; -}; - -class ActivePictureUpdaterTest : public testing::Test { -protected: - SurfaceFlinger* flinger() { - if (!mFlingerSetup) { - mFlinger.setupMockScheduler(); - mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); - mFlinger.setupRenderEngine(std::make_unique<renderengine::mock::RenderEngine>()); - mFlingerSetup = true; - } - return mFlinger.flinger(); - } - -private: - TestableSurfaceFlinger mFlinger; - bool mFlingerSetup = false; -}; - -// Hack to workaround initializer lists not working for parcelables because parcelables inherit from -// Parcelable, which has a virtual destructor. -auto UnorderedElementsAre(std::initializer_list<std::tuple<int32_t, int32_t, int64_t>> tuples) { - std::vector<ActivePicture> activePictures; - for (auto tuple : tuples) { - ActivePicture ap; - ap.layerId = std::get<0>(tuple); - ap.ownerUid = std::get<1>(tuple); - ap.pictureProfileId = std::get<2>(tuple); - activePictures.push_back(ap); - } - return testing::UnorderedElementsAreArray(activePictures); -} - -// Parcelables don't define this for matchers, which is unfortunate -void PrintTo(const ActivePicture& activePicture, std::ostream* os) { - *os << activePicture.toString(); -} - -TEST_F(ActivePictureUpdaterTest, notCalledWithNoProfile) { - sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100); - TestableLayerFE layerFE; - EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); - - ActivePictureUpdater updater; - { - layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE; - updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); - - ASSERT_FALSE(updater.updateAndHasChanged()); - } -} - -TEST_F(ActivePictureUpdaterTest, calledWhenLayerStartsUsingProfile) { - sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100); - TestableLayerFE layerFE; - EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); - - ActivePictureUpdater updater; - { - layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE; - updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); - - ASSERT_FALSE(updater.updateAndHasChanged()); - } - { - layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE.onPictureProfileCommitted(); - updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); - } -} - -TEST_F(ActivePictureUpdaterTest, notCalledWhenLayerContinuesUsingProfile) { - sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100); - TestableLayerFE layerFE; - EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); - - ActivePictureUpdater updater; - { - layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE.onPictureProfileCommitted(); - updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); - } - { - layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE.onPictureProfileCommitted(); - updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); - - ASSERT_FALSE(updater.updateAndHasChanged()); - } -} - -TEST_F(ActivePictureUpdaterTest, calledWhenLayerStopsUsingProfile) { - sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100); - TestableLayerFE layerFE; - EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); - - ActivePictureUpdater updater; - { - layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE.onPictureProfileCommitted(); - updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); - } - { - layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE; - updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({})); - } -} - -TEST_F(ActivePictureUpdaterTest, calledWhenLayerChangesProfile) { - sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100); - TestableLayerFE layerFE; - EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); - - ActivePictureUpdater updater; - { - layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE.onPictureProfileCommitted(); - updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); - } - { - layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2); - layerFE.onPictureProfileCommitted(); - updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 2}})); - } -} - -TEST_F(ActivePictureUpdaterTest, notCalledWhenUncommittedLayerChangesProfile) { - sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100); - TestableLayerFE layerFE1; - EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); - - sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200); - TestableLayerFE layerFE2; - EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20))); - - ActivePictureUpdater updater; - { - layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE1.onPictureProfileCommitted(); - updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); - - layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); - updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); - } - { - layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE1.onPictureProfileCommitted(); - updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); - - layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2); - updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); - - ASSERT_FALSE(updater.updateAndHasChanged()); - } -} - -TEST_F(ActivePictureUpdaterTest, calledWhenDifferentLayerUsesSameProfile) { - sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100); - TestableLayerFE layerFE1; - EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); - - sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200); - TestableLayerFE layerFE2; - EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20))); - - ActivePictureUpdater updater; - { - layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE1.onPictureProfileCommitted(); - updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); - - layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2); - layerFE2.onPictureProfileCommitted(); - updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), - UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}})); - } - { - layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2); - layerFE1.onPictureProfileCommitted(); - updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); - - layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE2.onPictureProfileCommitted(); - updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), - UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}})); - } -} - -TEST_F(ActivePictureUpdaterTest, calledWhenSameUidUsesSameProfile) { - sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100); - TestableLayerFE layerFE1; - EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); - - sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200); - TestableLayerFE layerFE2; - EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); - - ActivePictureUpdater updater; - { - layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE1.onPictureProfileCommitted(); - updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); - - layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2); - layerFE2.onPictureProfileCommitted(); - updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), - UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}})); - } - { - layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2); - layerFE1.onPictureProfileCommitted(); - updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); - - layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE2.onPictureProfileCommitted(); - updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), - UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}})); - } -} - -TEST_F(ActivePictureUpdaterTest, calledWhenNewLayerUsesSameProfile) { - sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100); - TestableLayerFE layerFE1; - EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); - - ActivePictureUpdater updater; - { - layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE1.onPictureProfileCommitted(); - updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}})); - } - - sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200); - TestableLayerFE layerFE2; - EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10))); - - { - layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE1.onPictureProfileCommitted(); - updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult()); - - layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1); - layerFE2.onPictureProfileCommitted(); - updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult()); - - ASSERT_TRUE(updater.updateAndHasChanged()); - EXPECT_THAT(updater.getActivePictures(), - UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}})); - } -} - -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index 53a9062a5a..f3d6dccc17 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -584,7 +584,7 @@ TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_vrr) { auto layer = createLegacyAndFrontedEndLayer(1); showLayer(1); - setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE, + setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); setFrameRateCategory(1, 0); @@ -623,7 +623,7 @@ TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_nonVrr) { auto layer = createLegacyAndFrontedEndLayer(1); showLayer(1); - setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE, + setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); setFrameRateCategory(1, 0); @@ -662,7 +662,7 @@ TEST_F(LayerHistoryIntegrationTest, oneLayerGteNoVote_arr) { auto layer = createLegacyAndFrontedEndLayer(1); showLayer(1); - setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE, + setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); EXPECT_EQ(1u, layerCount()); @@ -694,7 +694,7 @@ TEST_F(LayerHistoryIntegrationTest, oneLayerGteNoVote_mrr) { auto layer = createLegacyAndFrontedEndLayer(1); showLayer(1); - setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE, + setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_AT_LEAST, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); setFrameRateCategory(1, 0); diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 8c53eef01a..4d322efe86 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -2021,16 +2021,13 @@ TEST_F(LayerSnapshotTest, contentDirtyWhenParentGeometryChanges) { EXPECT_FALSE(getSnapshot(1)->contentDirty); } TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) { - if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { - GTEST_SKIP() << "Flag disabled, skipping test"; - } std::vector<TransactionState> transactions; transactions.emplace_back(); transactions.back().states.push_back({}); - transactions.back().states.front().layerId = 1; - transactions.back().states.front().state.layerId = 1; - transactions.back().states.front().state.what = layer_state_t::ePictureProfileHandleChanged; - transactions.back().states.front().state.pictureProfileHandle = PictureProfileHandle(3); + transactions.back().states.back().layerId = 1; + transactions.back().states.back().state.layerId = 1; + transactions.back().states.back().state.what = layer_state_t::ePictureProfileHandleChanged; + transactions.back().states.back().state.pictureProfileHandle = PictureProfileHandle(3); mLifecycleManager.applyTransactions(transactions); EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content); @@ -2042,23 +2039,50 @@ TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) { } TEST_F(LayerSnapshotTest, shouldUpdatePictureProfilePriorityFromAppContentPriority) { - if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { - GTEST_SKIP() << "Flag disabled, skipping test"; + { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.back().layerId = 1; + transactions.back().states.back().state.layerId = 1; + transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged; + transactions.back().states.back().state.appContentPriority = 1; + transactions.back().states.push_back({}); + transactions.back().states.back().layerId = 2; + transactions.back().states.back().state.layerId = 2; + transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged; + transactions.back().states.back().state.appContentPriority = -1; + + mLifecycleManager.applyTransactions(transactions); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content); + + update(mSnapshotBuilder); + + EXPECT_GT(getSnapshot(1)->pictureProfilePriority, getSnapshot(2)->pictureProfilePriority); + EXPECT_EQ(getSnapshot(1)->pictureProfilePriority - getSnapshot(2)->pictureProfilePriority, + 2); + } + { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.back().layerId = 1; + transactions.back().states.back().state.layerId = 1; + transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged; + transactions.back().states.back().state.appContentPriority = INT_MIN; + transactions.back().states.push_back({}); + transactions.back().states.back().layerId = 2; + transactions.back().states.back().state.layerId = 2; + transactions.back().states.back().state.what = layer_state_t::eAppContentPriorityChanged; + transactions.back().states.back().state.appContentPriority = INT_MAX; + + mLifecycleManager.applyTransactions(transactions); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content); + + update(mSnapshotBuilder); + + EXPECT_GT(getSnapshot(2)->pictureProfilePriority, getSnapshot(1)->pictureProfilePriority); } - std::vector<TransactionState> transactions; - transactions.emplace_back(); - transactions.back().states.push_back({}); - transactions.back().states.front().layerId = 1; - transactions.back().states.front().state.layerId = 1; - transactions.back().states.front().state.what = layer_state_t::eAppContentPriorityChanged; - transactions.back().states.front().state.appContentPriority = 3; - - mLifecycleManager.applyTransactions(transactions); - EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content); - - update(mSnapshotBuilder); - - EXPECT_EQ(getSnapshot(1)->pictureProfilePriority, 3); } } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 0d5266e113..2bf66ac54e 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -190,6 +190,9 @@ public: MOCK_METHOD(Error, getMaxLayerPictureProfiles, (Display, int32_t*)); MOCK_METHOD(Error, setDisplayPictureProfileId, (Display, PictureProfileId id)); MOCK_METHOD(Error, setLayerPictureProfileId, (Display, Layer, PictureProfileId id)); + MOCK_METHOD(Error, getLuts, + (Display, const std::vector<sp<GraphicBuffer>>&, + std::vector<aidl::android::hardware::graphics::composer3::Luts>*)); }; } // namespace Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index ec065a773c..4ca6fe073b 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -116,6 +116,10 @@ public: MOCK_METHOD(hal::Error, getMaxLayerPictureProfiles, (int32_t*), (override)); MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&), (override)); + MOCK_METHOD(hal::Error, getLuts, + (const std::vector<android::sp<android::GraphicBuffer>>&, + std::vector<aidl::android::hardware::graphics::composer3::Luts>*), + (override)); }; class Layer : public HWC2::Layer { diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h index 88f83d2e07..7bd85cd59f 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h @@ -151,6 +151,9 @@ public: MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (PhysicalDisplayId)); MOCK_METHOD(status_t, setDisplayPictureProfileHandle, (PhysicalDisplayId, const PictureProfileHandle&)); + MOCK_METHOD(status_t, getLuts, + (PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&, + std::vector<aidl::android::hardware::graphics::composer3::Luts>*)); }; } // namespace android::mock |