diff options
Diffstat (limited to 'libs')
246 files changed, 15480 insertions, 3752 deletions
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp index 28d0e4f9c3..49a94146db 100644 --- a/libs/binder/ActivityManager.cpp +++ b/libs/binder/ActivityManager.cpp @@ -89,6 +89,15 @@ bool ActivityManager::isUidActive(const uid_t uid, const String16& callingPackag return false; } +int32_t ActivityManager::getUidProcessState(const uid_t uid, const String16& callingPackage) +{ + sp<IActivityManager> service = getService(); + if (service != nullptr) { + return service->getUidProcessState(uid, callingPackage); + } + return PROCESS_STATE_UNKNOWN; +} + status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) { sp<IActivityManager> service = getService(); if (service != nullptr) { diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index da10687476..aedf6b0d18 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -107,6 +107,7 @@ cc_library_shared { "-Wall", "-Wextra", "-Werror", + "-Wzero-as-null-pointer-constant", ], product_variables: { binder32bit: { diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp index 428db4d579..377f604d44 100644 --- a/libs/binder/IActivityManager.cpp +++ b/libs/binder/IActivityManager.cpp @@ -17,8 +17,8 @@ #include <unistd.h> #include <fcntl.h> +#include <binder/ActivityManager.h> #include <binder/IActivityManager.h> - #include <binder/Parcel.h> namespace android { @@ -90,6 +90,20 @@ public: if (reply.readExceptionCode() != 0) return false; return reply.readInt32() == 1; } + + virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) + { + Parcel data, reply; + data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); + data.writeInt32(uid); + data.writeString16(callingPackage); + remote()->transact(GET_UID_PROCESS_STATE_TRANSACTION, data, &reply); + // fail on exception + if (reply.readExceptionCode() != 0) { + return ActivityManager::PROCESS_STATE_UNKNOWN; + } + return reply.readInt32(); + } }; // ------------------------------------------------------------------------------------ diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index 307bc28c0e..caf2318281 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -86,7 +86,7 @@ public: virtual void* getBase() const; virtual size_t getSize() const; virtual uint32_t getFlags() const; - virtual uint32_t getOffset() const; + off_t getOffset() const override; private: friend class IMemory; @@ -113,7 +113,7 @@ private: mutable void* mBase; mutable size_t mSize; mutable uint32_t mFlags; - mutable uint32_t mOffset; + mutable off_t mOffset; mutable bool mRealHeap; mutable Mutex mLock; }; @@ -189,13 +189,16 @@ sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { sp<IBinder> heap = reply.readStrongBinder(); - ssize_t o = reply.readInt32(); - size_t s = reply.readInt32(); if (heap != nullptr) { mHeap = interface_cast<IMemoryHeap>(heap); if (mHeap != nullptr) { + const int64_t offset64 = reply.readInt64(); + const uint64_t size64 = reply.readUint64(); + const ssize_t o = (ssize_t)offset64; + const size_t s = (size_t)size64; size_t heapSize = mHeap->getSize(); - if (s <= heapSize + if (s == size64 && o == offset64 // ILP32 bounds check + && s <= heapSize && o >= 0 && (static_cast<size_t>(o) <= heapSize - s)) { mOffset = o; @@ -236,8 +239,8 @@ status_t BnMemory::onTransact( ssize_t offset; size_t size; reply->writeStrongBinder( IInterface::asBinder(getMemory(&offset, &size)) ); - reply->writeInt32(offset); - reply->writeInt32(size); + reply->writeInt64(offset); + reply->writeUint64(size); return NO_ERROR; } break; default: @@ -316,18 +319,23 @@ void BpMemoryHeap::assertReallyMapped() const data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); status_t err = remote()->transact(HEAP_ID, data, &reply); int parcel_fd = reply.readFileDescriptor(); - ssize_t size = reply.readInt32(); - uint32_t flags = reply.readInt32(); - uint32_t offset = reply.readInt32(); - - ALOGE_IF(err, "binder=%p transaction failed fd=%d, size=%zd, err=%d (%s)", - IInterface::asBinder(this).get(), - parcel_fd, size, err, strerror(-err)); + const uint64_t size64 = reply.readUint64(); + const int64_t offset64 = reply.readInt64(); + const uint32_t flags = reply.readUint32(); + const size_t size = (size_t)size64; + const off_t offset = (off_t)offset64; + if (err != NO_ERROR || // failed transaction + size != size64 || offset != offset64) { // ILP32 size check + ALOGE("binder=%p transaction failed fd=%d, size=%zu, err=%d (%s)", + IInterface::asBinder(this).get(), + parcel_fd, size, err, strerror(-err)); + return; + } Mutex::Autolock _l(mLock); if (mHeapId.load(memory_order_relaxed) == -1) { int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0); - ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)", + ALOGE_IF(fd == -1, "cannot dup fd=%d, size=%zu, err=%d (%s)", parcel_fd, size, err, strerror(errno)); int access = PROT_READ; @@ -337,7 +345,7 @@ void BpMemoryHeap::assertReallyMapped() const mRealHeap = true; mBase = mmap(nullptr, size, access, MAP_SHARED, fd, offset); if (mBase == MAP_FAILED) { - ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zd, fd=%d (%s)", + ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zu, fd=%d (%s)", IInterface::asBinder(this).get(), size, fd, strerror(errno)); close(fd); } else { @@ -371,7 +379,7 @@ uint32_t BpMemoryHeap::getFlags() const { return mFlags; } -uint32_t BpMemoryHeap::getOffset() const { +off_t BpMemoryHeap::getOffset() const { assertMapped(); return mOffset; } @@ -394,9 +402,9 @@ status_t BnMemoryHeap::onTransact( case HEAP_ID: { CHECK_INTERFACE(IMemoryHeap, data, reply); reply->writeFileDescriptor(getHeapID()); - reply->writeInt32(getSize()); - reply->writeInt32(getFlags()); - reply->writeInt32(getOffset()); + reply->writeUint64(getSize()); + reply->writeInt64(getOffset()); + reply->writeUint32(getFlags()); return NO_ERROR; } break; default: diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index f052bcb982..8df83f1afe 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -109,6 +109,8 @@ static const char *kCommandStrings[] = { "BC_DEAD_BINDER_DONE" }; +static const int64_t kWorkSourcePropagatedBitIndex = 32; + static const char* getReturnString(uint32_t cmd) { size_t idx = cmd & 0xff; @@ -383,6 +385,48 @@ int32_t IPCThreadState::getStrictModePolicy() const return mStrictModePolicy; } +int64_t IPCThreadState::setCallingWorkSourceUid(uid_t uid) +{ + int64_t token = setCallingWorkSourceUidWithoutPropagation(uid); + mPropagateWorkSource = true; + return token; +} + +int64_t IPCThreadState::setCallingWorkSourceUidWithoutPropagation(uid_t uid) +{ + const int64_t propagatedBit = ((int64_t)mPropagateWorkSource) << kWorkSourcePropagatedBitIndex; + int64_t token = propagatedBit | mWorkSource; + mWorkSource = uid; + return token; +} + +void IPCThreadState::clearPropagateWorkSource() +{ + mPropagateWorkSource = false; +} + +bool IPCThreadState::shouldPropagateWorkSource() const +{ + return mPropagateWorkSource; +} + +uid_t IPCThreadState::getCallingWorkSourceUid() const +{ + return mWorkSource; +} + +int64_t IPCThreadState::clearCallingWorkSource() +{ + return setCallingWorkSourceUid(kUnsetWorkSource); +} + +void IPCThreadState::restoreCallingWorkSource(int64_t token) +{ + uid_t uid = (int)token; + setCallingWorkSourceUidWithoutPropagation(uid); + mPropagateWorkSource = ((token >> kWorkSourcePropagatedBitIndex) & 1) == 1; +} + void IPCThreadState::setLastTransactionBinderFlags(int32_t flags) { mLastTransactionBinderFlags = flags; @@ -736,6 +780,8 @@ status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) IPCThreadState::IPCThreadState() : mProcess(ProcessState::self()), + mWorkSource(kUnsetWorkSource), + mPropagateWorkSource(false), mStrictModePolicy(0), mLastTransactionBinderFlags(0) { @@ -1098,6 +1144,13 @@ status_t IPCThreadState::executeCommand(int32_t cmd) const uid_t origUid = mCallingUid; const int32_t origStrictModePolicy = mStrictModePolicy; const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags; + const int32_t origWorkSource = mWorkSource; + const bool origPropagateWorkSet = mPropagateWorkSource; + // Calling work source will be set by Parcel#enforceInterface. Parcel#enforceInterface + // is only guaranteed to be called for AIDL-generated stubs so we reset the work source + // here to never propagate it. + clearCallingWorkSource(); + clearPropagateWorkSource(); mCallingPid = tr.sender_pid; mCallingUid = tr.sender_euid; @@ -1150,6 +1203,8 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingUid = origUid; mStrictModePolicy = origStrictModePolicy; mLastTransactionBinderFlags = origTransactionBinderFlags; + mWorkSource = origWorkSource; + mPropagateWorkSource = origPropagateWorkSet; IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp index 697e948a6d..82f9047595 100644 --- a/libs/binder/IUidObserver.cpp +++ b/libs/binder/IUidObserver.cpp @@ -55,6 +55,16 @@ public: data.writeInt32(disabled ? 1 : 0); remote()->transact(ON_UID_IDLE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); } + + virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq) + { + Parcel data, reply; + data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor()); + data.writeInt32((int32_t) uid); + data.writeInt32(procState); + data.writeInt64(procStateSeq); + remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); + } }; // ---------------------------------------------------------------------- @@ -89,6 +99,14 @@ status_t BnUidObserver::onTransact( onUidIdle(uid, disabled); return NO_ERROR; } break; + case ON_UID_STATE_CHANGED_TRANSACTION: { + CHECK_INTERFACE(IUidObserver, data, reply); + uid_t uid = data.readInt32(); + int32_t procState = data.readInt32(); + int64_t procStateSeq = data.readInt64(); + onUidStateChanged(uid, procState, procStateSeq); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 9850ad9624..4c300b47c6 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -76,7 +76,7 @@ MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) } } -MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset) +MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, off_t offset) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { @@ -85,7 +85,7 @@ MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t off mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset); } -status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device) +status_t MemoryHeapBase::init(int fd, void *base, size_t size, int flags, const char* device) { if (mFD != -1) { return INVALID_OPERATION; @@ -98,13 +98,20 @@ status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const cha return NO_ERROR; } -status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) +status_t MemoryHeapBase::mapfd(int fd, size_t size, off_t offset) { if (size == 0) { // try to figure out the size automatically struct stat sb; - if (fstat(fd, &sb) == 0) - size = sb.st_size; + if (fstat(fd, &sb) == 0) { + size = (size_t)sb.st_size; + // sb.st_size is off_t which on ILP32 may be 64 bits while size_t is 32 bits. + if ((off_t)size != sb.st_size) { + ALOGE("%s: size of file %lld cannot fit in memory", + __func__, (long long)sb.st_size); + return INVALID_OPERATION; + } + } // if it didn't work, let mmap() fail. } @@ -112,12 +119,12 @@ status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) void* base = (uint8_t*)mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); if (base == MAP_FAILED) { - ALOGE("mmap(fd=%d, size=%u) failed (%s)", - fd, uint32_t(size), strerror(errno)); + ALOGE("mmap(fd=%d, size=%zu) failed (%s)", + fd, size, strerror(errno)); close(fd); return -errno; } - //ALOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); + //ALOGD("mmap(fd=%d, base=%p, size=%zu)", fd, base, size); mBase = base; mNeedUnmap = true; } else { @@ -140,7 +147,7 @@ void MemoryHeapBase::dispose() int fd = android_atomic_or(-1, &mFD); if (fd >= 0) { if (mNeedUnmap) { - //ALOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize); + //ALOGD("munmap(fd=%d, base=%p, size=%zu)", fd, mBase, mSize); munmap(mBase, mSize); } mBase = nullptr; @@ -169,7 +176,7 @@ const char* MemoryHeapBase::getDevice() const { return mDevice; } -uint32_t MemoryHeapBase::getOffset() const { +off_t MemoryHeapBase::getOffset() const { return mOffset; } diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index b2db945af0..d285030c1b 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -74,7 +74,7 @@ static size_t pad_size(size_t s) { } // Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER -#define STRICT_MODE_PENALTY_GATHER (0x40 << 16) +#define STRICT_MODE_PENALTY_GATHER (1 << 31) // XXX This can be made public if we want to provide // support for typed data. @@ -599,8 +599,10 @@ bool Parcel::hasFileDescriptors() const // Write RPC headers. (previously just the interface token) status_t Parcel::writeInterfaceToken(const String16& interface) { - writeInt32(IPCThreadState::self()->getStrictModePolicy() | - STRICT_MODE_PENALTY_GATHER); + const IPCThreadState* threadState = IPCThreadState::self(); + writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); + writeInt32(threadState->shouldPropagateWorkSource() ? + threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource); // currently the interface identification token is just its name as a string return writeString16(interface); } @@ -613,6 +615,7 @@ bool Parcel::checkInterface(IBinder* binder) const bool Parcel::enforceInterface(const String16& interface, IPCThreadState* threadState) const { + // StrictModePolicy. int32_t strictPolicy = readInt32(); if (threadState == nullptr) { threadState = IPCThreadState::self(); @@ -627,6 +630,10 @@ bool Parcel::enforceInterface(const String16& interface, } else { threadState->setStrictModePolicy(strictPolicy); } + // WorkSource. + int32_t workSource = readInt32(); + threadState->setCallingWorkSourceUidWithoutPropagation(workSource); + // Interface descriptor. const String16 str(readString16()); if (str == interface) { return true; @@ -871,6 +878,16 @@ status_t Parcel::writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& v return writeNullableTypedVector(val, &Parcel::writeInt64); } +status_t Parcel::writeUint64Vector(const std::vector<uint64_t>& val) +{ + return writeTypedVector(val, &Parcel::writeUint64); +} + +status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val) +{ + return writeNullableTypedVector(val, &Parcel::writeUint64); +} + status_t Parcel::writeFloatVector(const std::vector<float>& val) { return writeTypedVector(val, &Parcel::writeFloat); @@ -1732,6 +1749,14 @@ status_t Parcel::readInt64Vector(std::vector<int64_t>* val) const { return readTypedVector(val, &Parcel::readInt64); } +status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const { + return readNullableTypedVector(val, &Parcel::readUint64); +} + +status_t Parcel::readUint64Vector(std::vector<uint64_t>* val) const { + return readTypedVector(val, &Parcel::readUint64); +} + status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const { return readNullableTypedVector(val, &Parcel::readFloat); } @@ -2329,6 +2354,15 @@ status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const int fd = readFileDescriptor(); if (fd == int(BAD_TYPE)) return BAD_VALUE; + if (!ashmem_valid(fd)) { + ALOGE("invalid fd"); + return BAD_VALUE; + } + int size = ashmem_get_size_region(fd); + if (size < 0 || size_t(size) < len) { + ALOGE("request size %zu does not match fd size %d", len, size); + return BAD_VALUE; + } void* ptr = ::mmap(nullptr, len, isMutable ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) return NO_MEMORY; diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h index b8db09145f..26dafd0a12 100644 --- a/libs/binder/include/binder/ActivityManager.h +++ b/libs/binder/include/binder/ActivityManager.h @@ -31,6 +31,8 @@ class ActivityManager public: enum { + // Flag for registerUidObserver: report uid state changed + UID_OBSERVER_PROCSTATE = 1<<0, // Flag for registerUidObserver: report uid gone UID_OBSERVER_GONE = 1<<1, // Flag for registerUidObserver: report uid has become idle @@ -40,8 +42,27 @@ public: }; enum { - // Not a real process state - PROCESS_STATE_UNKNOWN = -1 + PROCESS_STATE_UNKNOWN = -1, + PROCESS_STATE_PERSISTENT = 0, + PROCESS_STATE_PERSISTENT_UI = 1, + PROCESS_STATE_TOP = 2, + PROCESS_STATE_FOREGROUND_SERVICE = 3, + PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 4, + PROCESS_STATE_IMPORTANT_FOREGROUND = 5, + PROCESS_STATE_IMPORTANT_BACKGROUND = 6, + PROCESS_STATE_TRANSIENT_BACKGROUND = 7, + PROCESS_STATE_BACKUP = 8, + PROCESS_STATE_SERVICE = 9, + PROCESS_STATE_RECEIVER = 10, + PROCESS_STATE_TOP_SLEEPING = 11, + PROCESS_STATE_HEAVY_WEIGHT = 12, + PROCESS_STATE_HOME = 13, + PROCESS_STATE_LAST_ACTIVITY = 14, + PROCESS_STATE_CACHED_ACTIVITY = 15, + PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 16, + PROCESS_STATE_CACHED_RECENT = 17, + PROCESS_STATE_CACHED_EMPTY = 18, + PROCESS_STATE_NONEXISTENT = 19, }; ActivityManager(); @@ -53,8 +74,10 @@ public: const String16& callingPackage); void unregisterUidObserver(const sp<IUidObserver>& observer); bool isUidActive(const uid_t uid, const String16& callingPackage); + int getUidProcessState(const uid_t uid, const String16& callingPackage); - status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient); + + status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient); status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient); private: diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h index c5b57c7edf..37237dfd48 100644 --- a/libs/binder/include/binder/AppOpsManager.h +++ b/libs/binder/include/binder/AppOpsManager.h @@ -95,6 +95,20 @@ public: OP_USE_FINGERPRINT = 55, OP_BODY_SENSORS = 56, OP_AUDIO_ACCESSIBILITY_VOLUME = 64, + OP_READ_PHONE_NUMBERS = 65, + OP_REQUEST_INSTALL_PACKAGES = 66, + OP_PICTURE_IN_PICTURE = 67, + OP_INSTANT_APP_START_FOREGROUND = 68, + OP_ANSWER_PHONE_CALLS = 69, + OP_RUN_ANY_IN_BACKGROUND = 70, + OP_CHANGE_WIFI_STATE = 71, + OP_REQUEST_DELETE_PACKAGES = 72, + OP_BIND_ACCESSIBILITY_SERVICE = 73, + OP_ACCEPT_HANDOVER = 74, + OP_MANAGE_IPSEC_TUNNELS = 75, + OP_START_FOREGROUND = 76, + OP_BLUETOOTH_SCAN = 77, + OP_USE_BIOMETRIC = 78, }; AppOpsManager(); diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h index f34969be51..6abc071c45 100644 --- a/libs/binder/include/binder/IActivityManager.h +++ b/libs/binder/include/binder/IActivityManager.h @@ -38,12 +38,14 @@ public: const String16& callingPackage) = 0; virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0; virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0; + virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0; enum { OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, REGISTER_UID_OBSERVER_TRANSACTION, UNREGISTER_UID_OBSERVER_TRANSACTION, - IS_UID_ACTIVE_TRANSACTION + IS_UID_ACTIVE_TRANSACTION, + GET_UID_PROCESS_STATE_TRANSACTION }; }; diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h index 14edcbe72a..1674516ca2 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -47,7 +47,7 @@ class IShellCallback; * (method calls, property get and set) is down through a low-level * protocol implemented on top of the transact() API. */ -class IBinder : public virtual RefBase +class [[clang::lto_visibility_public]] IBinder : public virtual RefBase { public: enum { diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h index db9f53a797..071946f50c 100644 --- a/libs/binder/include/binder/IMemory.h +++ b/libs/binder/include/binder/IMemory.h @@ -43,7 +43,7 @@ public: virtual void* getBase() const = 0; virtual size_t getSize() const = 0; virtual uint32_t getFlags() const = 0; - virtual uint32_t getOffset() const = 0; + virtual off_t getOffset() const = 0; // these are there just for backward source compatibility int32_t heapID() const { return getHeapID(); } diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 40b51adb06..26e8c0b599 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -47,6 +47,19 @@ public: void setStrictModePolicy(int32_t policy); int32_t getStrictModePolicy() const; + // See Binder#setCallingWorkSourceUid in Binder.java. + int64_t setCallingWorkSourceUid(uid_t uid); + // Internal only. Use setCallingWorkSourceUid(uid) instead. + int64_t setCallingWorkSourceUidWithoutPropagation(uid_t uid); + // See Binder#getCallingWorkSourceUid in Binder.java. + uid_t getCallingWorkSourceUid() const; + // See Binder#clearCallingWorkSource in Binder.java. + int64_t clearCallingWorkSource(); + // See Binder#restoreCallingWorkSource in Binder.java. + void restoreCallingWorkSource(int64_t token); + void clearPropagateWorkSource(); + bool shouldPropagateWorkSource() const; + void setLastTransactionBinderFlags(int32_t flags); int32_t getLastTransactionBinderFlags() const; @@ -118,6 +131,13 @@ public: // infer information about thread state. bool isServingCall() const; + // The work source represents the UID of the process we should attribute the transaction + // to. We use -1 to specify that the work source was not set using #setWorkSource. + // + // This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java + // side. + static const int32_t kUnsetWorkSource = -1; + private: IPCThreadState(); ~IPCThreadState(); @@ -155,6 +175,11 @@ private: status_t mLastError; pid_t mCallingPid; uid_t mCallingUid; + // The UID of the process who is responsible for this transaction. + // This is used for resource attribution. + int32_t mWorkSource; + // Whether the work source should be propagated. + bool mPropagateWorkSource; int32_t mStrictModePolicy; int32_t mLastTransactionBinderFlags; IPCThreadStateBase *mIPCThreadStateBase; diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index e5d8ea67de..aeea1a2676 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -88,7 +88,7 @@ status_t getService(const String16& name, sp<INTERFACE>* outService) const sp<IServiceManager> sm = defaultServiceManager(); if (sm != nullptr) { *outService = interface_cast<INTERFACE>(sm->getService(name)); - if ((*outService) != NULL) return NO_ERROR; + if ((*outService) != nullptr) return NO_ERROR; } return NAME_NOT_FOUND; } diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h index 9937ad6a29..a1f530dc71 100644 --- a/libs/binder/include/binder/IUidObserver.h +++ b/libs/binder/include/binder/IUidObserver.h @@ -34,11 +34,13 @@ public: virtual void onUidGone(uid_t uid, bool disabled) = 0; virtual void onUidActive(uid_t uid) = 0; virtual void onUidIdle(uid_t uid, bool disabled) = 0; + virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq) = 0; enum { ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, ON_UID_ACTIVE_TRANSACTION, - ON_UID_IDLE_TRANSACTION + ON_UID_IDLE_TRANSACTION, + ON_UID_STATE_CHANGED_TRANSACTION }; }; diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h index 4be20a0dbd..100d784a83 100644 --- a/libs/binder/include/binder/MemoryHeapBase.h +++ b/libs/binder/include/binder/MemoryHeapBase.h @@ -42,7 +42,7 @@ public: * maps the memory referenced by fd. but DOESN'T take ownership * of the filedescriptor (it makes a copy with dup() */ - MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0); + MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, off_t offset = 0); /* * maps memory from the given device @@ -64,7 +64,7 @@ public: virtual size_t getSize() const; virtual uint32_t getFlags() const; - virtual uint32_t getOffset() const; + off_t getOffset() const override; const char* getDevice() const; @@ -82,11 +82,11 @@ public: protected: MemoryHeapBase(); // init() takes ownership of fd - status_t init(int fd, void *base, int size, + status_t init(int fd, void *base, size_t size, int flags = 0, const char* device = nullptr); private: - status_t mapfd(int fd, size_t size, uint32_t offset = 0); + status_t mapfd(int fd, size_t size, off_t offset = 0); int mFD; size_t mSize; @@ -94,7 +94,7 @@ private: uint32_t mFlags; const char* mDevice; bool mNeedUnmap; - uint32_t mOffset; + off_t mOffset; }; // --------------------------------------------------------------------------- diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index c9c273acd8..cd151ee509 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -139,6 +139,8 @@ public: status_t writeInt32Vector(const std::vector<int32_t>& val); status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val); status_t writeInt64Vector(const std::vector<int64_t>& val); + status_t writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val); + status_t writeUint64Vector(const std::vector<uint64_t>& val); status_t writeFloatVector(const std::unique_ptr<std::vector<float>>& val); status_t writeFloatVector(const std::vector<float>& val); status_t writeDoubleVector(const std::unique_ptr<std::vector<double>>& val); @@ -313,6 +315,8 @@ public: status_t readInt32Vector(std::vector<int32_t>* val) const; status_t readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const; status_t readInt64Vector(std::vector<int64_t>* val) const; + status_t readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const; + status_t readUint64Vector(std::vector<uint64_t>* val) const; status_t readFloatVector(std::unique_ptr<std::vector<float>>* val) const; status_t readFloatVector(std::vector<float>* val) const; status_t readDoubleVector(std::unique_ptr<std::vector<double>>* val) const; diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 99a71bd662..78f11594b9 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -71,6 +71,8 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION, BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, + BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, + BINDER_LIB_TEST_ECHO_VECTOR, }; pid_t start_server_process(int arg2, bool usePoll = false) @@ -179,6 +181,7 @@ class BinderLibTest : public ::testing::Test { public: virtual void SetUp() { m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer(); + IPCThreadState::self()->restoreCallingWorkSource(0); } virtual void TearDown() { } @@ -938,6 +941,141 @@ TEST_F(BinderLibTest, OnewayQueueing) EXPECT_EQ(NO_ERROR, ret); } +TEST_F(BinderLibTest, WorkSourceUnsetByDefault) +{ + status_t ret; + Parcel data, reply; + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(-1, reply.readInt32()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceSet) +{ + status_t ret; + Parcel data, reply; + IPCThreadState::self()->clearCallingWorkSource(); + int64_t previousWorkSource = IPCThreadState::self()->setCallingWorkSourceUid(100); + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(100, reply.readInt32()); + EXPECT_EQ(-1, previousWorkSource); + EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceSetWithoutPropagation) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUidWithoutPropagation(100); + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); + + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(-1, reply.readInt32()); + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceCleared) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUid(100); + int64_t token = IPCThreadState::self()->clearCallingWorkSource(); + int32_t previousWorkSource = (int32_t)token; + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + + EXPECT_EQ(-1, reply.readInt32()); + EXPECT_EQ(100, previousWorkSource); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceRestored) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUid(100); + int64_t token = IPCThreadState::self()->clearCallingWorkSource(); + IPCThreadState::self()->restoreCallingWorkSource(token); + + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + + EXPECT_EQ(100, reply.readInt32()); + EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, PropagateFlagSet) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->clearPropagateWorkSource(); + IPCThreadState::self()->setCallingWorkSourceUid(100); + EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); +} + +TEST_F(BinderLibTest, PropagateFlagCleared) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUid(100); + IPCThreadState::self()->clearPropagateWorkSource(); + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); +} + +TEST_F(BinderLibTest, PropagateFlagRestored) +{ + status_t ret; + Parcel data, reply; + + int token = IPCThreadState::self()->setCallingWorkSourceUid(100); + IPCThreadState::self()->restoreCallingWorkSource(token); + + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); +} + +TEST_F(BinderLibTest, WorkSourcePropagatedForAllFollowingBinderCalls) +{ + IPCThreadState::self()->setCallingWorkSourceUid(100); + + Parcel data, reply; + status_t ret; + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + + Parcel data2, reply2; + status_t ret2; + data2.writeInterfaceToken(binderLibTestServiceName); + ret2 = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data2, &reply2); + EXPECT_EQ(100, reply2.readInt32()); + EXPECT_EQ(NO_ERROR, ret2); +} + +TEST_F(BinderLibTest, VectorSent) { + Parcel data, reply; + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + + std::vector<uint64_t> const testValue = { std::numeric_limits<uint64_t>::max(), 0, 200 }; + data.writeUint64Vector(testValue); + + status_t ret = server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply); + EXPECT_EQ(NO_ERROR, ret); + std::vector<uint64_t> readValue; + ret = reply.readUint64Vector(&readValue); + EXPECT_EQ(readValue, testValue); +} + class BinderLibTestService : public BBinder { public: @@ -1236,6 +1374,19 @@ class BinderLibTestService : public BBinder } return NO_ERROR; } + case BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION: { + data.enforceInterface(binderLibTestServiceName); + reply->writeInt32(IPCThreadState::self()->getCallingWorkSourceUid()); + return NO_ERROR; + } + case BINDER_LIB_TEST_ECHO_VECTOR: { + std::vector<uint64_t> vector; + auto err = data.readUint64Vector(&vector); + if (err != NO_ERROR) + return err; + reply->writeUint64Vector(vector); + return NO_ERROR; + } default: return UNKNOWN_TRANSACTION; }; diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 35296a96c1..04884bb3ec 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -46,6 +46,7 @@ static const char* hal_interfaces_to_dump[] { "android.hardware.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.drm@1.0::IDrmFactory", + "android.hardware.graphics.allocator@2.0::IAllocator", "android.hardware.graphics.composer@2.1::IComposer", "android.hardware.health@2.0::IHealth", "android.hardware.media.omx@1.0::IOmx", diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index 4da30e9980..280c14a3ea 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -22,6 +22,8 @@ cc_library_shared { cflags: ["-Wall", "-Werror"], shared_libs: [ + "libbase", + "libcutils", "liblog", ], diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index a8ef7a051d..9dc7431714 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -18,51 +18,330 @@ #define LOG_TAG "GraphicsEnv" #include <graphicsenv/GraphicsEnv.h> -#include <mutex> +#include <dlfcn.h> +#include <unistd.h> +#include <android-base/file.h> +#include <android-base/properties.h> +#include <android-base/strings.h> #include <android/dlext.h> +#include <cutils/properties.h> #include <log/log.h> +#include <sys/prctl.h> + +#include <memory> +#include <mutex> +#include <string> + +#include <dlfcn.h> // TODO(b/37049319) Get this from a header once one exists extern "C" { - android_namespace_t* android_get_exported_namespace(const char*); - android_namespace_t* android_create_namespace(const char* name, - const char* ld_library_path, - const char* default_library_path, - uint64_t type, - const char* permitted_when_isolated_path, - android_namespace_t* parent); +android_namespace_t* android_get_exported_namespace(const char*); +android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, + const char* default_library_path, uint64_t type, + const char* permitted_when_isolated_path, + android_namespace_t* parent); +bool android_link_namespaces(android_namespace_t* from, android_namespace_t* to, + const char* shared_libs_sonames); - enum { - ANDROID_NAMESPACE_TYPE_ISOLATED = 1, - ANDROID_NAMESPACE_TYPE_SHARED = 2, - }; +enum { + ANDROID_NAMESPACE_TYPE_ISOLATED = 1, + ANDROID_NAMESPACE_TYPE_SHARED = 2, +}; } +// TODO(ianelliott@): Get the following from an ANGLE header: +#define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting +// Version-2 API: +typedef bool (*fpANGLEGetFeatureSupportUtilAPIVersion)(unsigned int* versionToUse); +typedef bool (*fpANGLEAndroidParseRulesString)(const char* rulesString, void** rulesHandle, + int* rulesVersion); +typedef bool (*fpANGLEGetSystemInfo)(void** handle); +typedef bool (*fpANGLEAddDeviceInfoToSystemInfo)(const char* deviceMfr, const char* deviceModel, + void* handle); +typedef bool (*fpANGLEShouldBeUsedForApplication)(void* rulesHandle, int rulesVersion, + void* systemInfoHandle, const char* appName); +typedef bool (*fpANGLEFreeRulesHandle)(void* handle); +typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle); + namespace android { +enum NativeLibrary { + LLNDK = 0, + VNDKSP = 1, +}; + +static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/etc/llndk.libraries.txt", + "/etc/vndksp.libraries.txt"}; + +static std::string vndkVersionStr() { +#ifdef __BIONIC__ + std::string version = android::base::GetProperty("ro.vndk.version", ""); + if (version != "" && version != "current") { + return "." + version; + } +#endif + return ""; +} + +static void insertVndkVersionStr(std::string* fileName) { + LOG_ALWAYS_FATAL_IF(!fileName, "fileName should never be nullptr"); + size_t insertPos = fileName->find_last_of("."); + if (insertPos == std::string::npos) { + insertPos = fileName->length(); + } + fileName->insert(insertPos, vndkVersionStr()); +} + +static bool readConfig(const std::string& configFile, std::vector<std::string>* soNames) { + // Read list of public native libraries from the config file. + std::string fileContent; + if (!base::ReadFileToString(configFile, &fileContent)) { + return false; + } + + std::vector<std::string> lines = base::Split(fileContent, "\n"); + + for (auto& line : lines) { + auto trimmedLine = base::Trim(line); + if (!trimmedLine.empty()) { + soNames->push_back(trimmedLine); + } + } + + return true; +} + +static const std::string getSystemNativeLibraries(NativeLibrary type) { + static const char* androidRootEnv = getenv("ANDROID_ROOT"); + static const std::string rootDir = androidRootEnv != nullptr ? androidRootEnv : "/system"; + + std::string nativeLibrariesSystemConfig = rootDir + kNativeLibrariesSystemConfigPath[type]; + + insertVndkVersionStr(&nativeLibrariesSystemConfig); + + std::vector<std::string> soNames; + if (!readConfig(nativeLibrariesSystemConfig, &soNames)) { + ALOGE("Failed to retrieve library names from %s", nativeLibrariesSystemConfig.c_str()); + return ""; + } + + return base::Join(soNames, ':'); +} + /*static*/ GraphicsEnv& GraphicsEnv::getInstance() { static GraphicsEnv env; return env; } +int GraphicsEnv::getCanLoadSystemLibraries() { + if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { + // Return an integer value since this crosses library boundaries + return 1; + } + return 0; +} + void GraphicsEnv::setDriverPath(const std::string path) { if (!mDriverPath.empty()) { - ALOGV("ignoring attempt to change driver path from '%s' to '%s'", - mDriverPath.c_str(), path.c_str()); + ALOGV("ignoring attempt to change driver path from '%s' to '%s'", mDriverPath.c_str(), + path.c_str()); return; } ALOGV("setting driver path to '%s'", path.c_str()); mDriverPath = path; } +void* GraphicsEnv::loadLibrary(std::string name) { + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = getAngleNamespace(), + }; + + std::string libName = std::string("lib") + name + "_angle.so"; + + void* so = android_dlopen_ext(libName.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); + + if (so) { + ALOGD("dlopen_ext from APK (%s) success at %p", libName.c_str(), so); + return so; + } else { + ALOGE("dlopen_ext(\"%s\") failed: %s", libName.c_str(), dlerror()); + } + + return nullptr; +} + +bool GraphicsEnv::checkAngleRules(void* so) { + char manufacturer[PROPERTY_VALUE_MAX]; + char model[PROPERTY_VALUE_MAX]; + property_get("ro.product.manufacturer", manufacturer, "UNSET"); + property_get("ro.product.model", model, "UNSET"); + + auto ANGLEGetFeatureSupportUtilAPIVersion = + (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so, + "ANGLEGetFeatureSupportUtilAPIVersion"); + + if (!ANGLEGetFeatureSupportUtilAPIVersion) { + ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function"); + return false; + } + + // Negotiate the interface version by requesting most recent known to the platform + unsigned int versionToUse = CURRENT_ANGLE_API_VERSION; + if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) { + ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, " + "requested version %u", + versionToUse); + return false; + } + + // Add and remove versions below as needed + bool useAngle = false; + switch (versionToUse) { + case 2: { + ALOGV("Using version %d of ANGLE feature-support library", versionToUse); + void* rulesHandle = nullptr; + int rulesVersion = 0; + void* systemInfoHandle = nullptr; + + // Get the symbols for the feature-support-utility library: +#define GET_SYMBOL(symbol) \ + fp##symbol symbol = (fp##symbol)dlsym(so, #symbol); \ + if (!symbol) { \ + ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \ + break; \ + } + GET_SYMBOL(ANGLEAndroidParseRulesString); + GET_SYMBOL(ANGLEGetSystemInfo); + GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo); + GET_SYMBOL(ANGLEShouldBeUsedForApplication); + GET_SYMBOL(ANGLEFreeRulesHandle); + GET_SYMBOL(ANGLEFreeSystemInfoHandle); + + // Parse the rules, obtain the SystemInfo, and evaluate the + // application against the rules: + if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) { + ALOGW("ANGLE feature-support library cannot parse rules file"); + break; + } + if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) { + ALOGW("ANGLE feature-support library cannot obtain SystemInfo"); + break; + } + if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) { + ALOGW("ANGLE feature-support library cannot add device info to SystemInfo"); + break; + } + useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion, + systemInfoHandle, mAngleAppName.c_str()); + (ANGLEFreeRulesHandle)(rulesHandle); + (ANGLEFreeSystemInfoHandle)(systemInfoHandle); + } break; + + default: + ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse); + } + + ALOGV("Close temporarily-loaded ANGLE opt-in/out logic"); + return useAngle; +} + +bool GraphicsEnv::shouldUseAngle(std::string appName) { + if (appName != mAngleAppName) { + // Make sure we are checking the app we were init'ed for + ALOGE("App name does not match: expected '%s', got '%s'", mAngleAppName.c_str(), + appName.c_str()); + return false; + } + + return shouldUseAngle(); +} + +bool GraphicsEnv::shouldUseAngle() { + // Make sure we are init'ed + if (mAngleAppName.empty()) { + ALOGE("App name is empty. setAngleInfo() must be called first to enable ANGLE."); + return false; + } + + return mUseAngle; +} + +void GraphicsEnv::updateUseAngle() { + mUseAngle = false; + + const char* ANGLE_PREFER_ANGLE = "angle"; + const char* ANGLE_PREFER_NATIVE = "native"; + + if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) { + ALOGV("User set \"Developer Options\" to force the use of ANGLE"); + mUseAngle = true; + } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) { + ALOGV("User set \"Developer Options\" to force the use of Native"); + mUseAngle = false; + } else { + // The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily + // load ANGLE and call the updatable opt-in/out logic: + + // Check if ANGLE is enabled. Workaround for several bugs: + // b/119305693 b/119322355 b/119305887 + // Something is not working correctly in the feature library + char prop[PROPERTY_VALUE_MAX]; + property_get("debug.angle.enable", prop, "0"); + void* featureSo = nullptr; + if (atoi(prop)) { + featureSo = loadLibrary("feature_support"); + } + if (featureSo) { + ALOGV("loaded ANGLE's opt-in/out logic from namespace"); + mUseAngle = checkAngleRules(featureSo); + dlclose(featureSo); + featureSo = nullptr; + } else { + ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE."); + } + } +} + +void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName, + const std::string developerOptIn, const int rulesFd, + const long rulesOffset, const long rulesLength) { + ALOGV("setting ANGLE path to '%s'", path.c_str()); + mAnglePath = path; + ALOGV("setting ANGLE app name to '%s'", appName.c_str()); + mAngleAppName = appName; + ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str()); + mAngleDeveloperOptIn = developerOptIn; + + lseek(rulesFd, rulesOffset, SEEK_SET); + mRulesBuffer = std::vector<char>(rulesLength + 1); + ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength); + if (numBytesRead < 0) { + ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead); + numBytesRead = 0; + } else if (numBytesRead == 0) { + ALOGW("Empty rules file"); + } + if (numBytesRead != rulesLength) { + ALOGW("Did not read all of the necessary bytes from the rules file." + "expected: %ld, got: %zd", + rulesLength, numBytesRead); + } + mRulesBuffer[numBytesRead] = '\0'; + + // Update the current status of whether we should use ANGLE or not + updateUseAngle(); +} + void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) { if (mLayerPaths.empty()) { mLayerPaths = layerPaths; mAppNamespace = appNamespace; } else { ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'", - layerPaths.c_str(), appNamespace); + layerPaths.c_str(), appNamespace); } } @@ -70,40 +349,87 @@ NativeLoaderNamespace* GraphicsEnv::getAppNamespace() { return mAppNamespace; } -const std::string GraphicsEnv::getLayerPaths(){ +std::string& GraphicsEnv::getAngleAppName() { + return mAngleAppName; +} + +const std::string& GraphicsEnv::getLayerPaths() { return mLayerPaths; } -const std::string GraphicsEnv::getDebugLayers() { +const std::string& GraphicsEnv::getDebugLayers() { return mDebugLayers; } +const std::string& GraphicsEnv::getDebugLayersGLES() { + return mDebugLayersGLES; +} + void GraphicsEnv::setDebugLayers(const std::string layers) { mDebugLayers = layers; } +void GraphicsEnv::setDebugLayersGLES(const std::string layers) { + mDebugLayersGLES = layers; +} + android_namespace_t* GraphicsEnv::getDriverNamespace() { static std::once_flag once; std::call_once(once, [this]() { - if (mDriverPath.empty()) - return; - // If the sphal namespace isn't configured for a device, don't support updatable drivers. - // We need a parent namespace to inherit the default search path from. - auto sphalNamespace = android_get_exported_namespace("sphal"); - if (!sphalNamespace) return; + if (mDriverPath.empty()) return; + + auto vndkNamespace = android_get_exported_namespace("vndk"); + if (!vndkNamespace) return; + mDriverNamespace = android_create_namespace("gfx driver", - nullptr, // ld_library_path + mDriverPath.c_str(), // ld_library_path mDriverPath.c_str(), // default_library_path - ANDROID_NAMESPACE_TYPE_SHARED | - ANDROID_NAMESPACE_TYPE_ISOLATED, + ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr, // permitted_when_isolated_path - sphalNamespace); + nullptr); + + const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK); + if (llndkLibraries.empty()) { + mDriverNamespace = nullptr; + return; + } + if (!android_link_namespaces(mDriverNamespace, nullptr, llndkLibraries.c_str())) { + ALOGE("Failed to link default namespace[%s]", dlerror()); + mDriverNamespace = nullptr; + return; + } + + const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP); + if (vndkspLibraries.empty()) { + mDriverNamespace = nullptr; + return; + } + if (!android_link_namespaces(mDriverNamespace, vndkNamespace, vndkspLibraries.c_str())) { + ALOGE("Failed to link vndk namespace[%s]", dlerror()); + mDriverNamespace = nullptr; + return; + } }); + return mDriverNamespace; } -} // namespace android +android_namespace_t* GraphicsEnv::getAngleNamespace() { + static std::once_flag once; + std::call_once(once, [this]() { + if (mAnglePath.empty()) return; + + mAngleNamespace = android_create_namespace("ANGLE", + nullptr, // ld_library_path + mAnglePath.c_str(), // default_library_path + ANDROID_NAMESPACE_TYPE_SHARED | + ANDROID_NAMESPACE_TYPE_ISOLATED, + nullptr, // permitted_when_isolated_path + nullptr); + if (!mAngleNamespace) ALOGD("Could not create ANGLE namespace from default"); + }); -extern "C" android_namespace_t* android_getDriverNamespace() { - return android::GraphicsEnv::getInstance().getDriverNamespace(); + return mAngleNamespace; } + +} // namespace android diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 17e8f6ba47..37c24ef173 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -18,6 +18,7 @@ #define ANDROID_UI_GRAPHICS_ENV_H 1 #include <string> +#include <vector> struct android_namespace_t; @@ -29,6 +30,8 @@ class GraphicsEnv { public: static GraphicsEnv& getInstance(); + int getCanLoadSystemLibraries(); + // Set a search path for loading graphics drivers. The path is a list of // directories separated by ':'. A directory can be contained in a zip file // (drivers must be stored uncompressed and page aligned); such elements @@ -37,35 +40,48 @@ public: void setDriverPath(const std::string path); android_namespace_t* getDriverNamespace(); + bool shouldUseAngle(std::string appName); + bool shouldUseAngle(); + // Set a search path for loading ANGLE libraries. The path is a list of + // directories separated by ':'. A directory can be contained in a zip file + // (libraries must be stored uncompressed and page aligned); such elements + // in the search path must have a '!' after the zip filename, e.g. + // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a + void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn, + const int rulesFd, const long rulesOffset, const long rulesLength); + android_namespace_t* getAngleNamespace(); + std::string& getAngleAppName(); + void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths); NativeLoaderNamespace* getAppNamespace(); - const std::string getLayerPaths(); + + const std::string& getLayerPaths(); void setDebugLayers(const std::string layers); - const std::string getDebugLayers(); + void setDebugLayersGLES(const std::string layers); + const std::string& getDebugLayers(); + const std::string& getDebugLayersGLES(); private: + void* loadLibrary(std::string name); + bool checkAngleRules(void* so); + void updateUseAngle(); + GraphicsEnv() = default; std::string mDriverPath; + std::string mAnglePath; + std::string mAngleAppName; + std::string mAngleDeveloperOptIn; + std::vector<char> mRulesBuffer; + bool mUseAngle; std::string mDebugLayers; + std::string mDebugLayersGLES; std::string mLayerPaths; android_namespace_t* mDriverNamespace = nullptr; + android_namespace_t* mAngleNamespace = nullptr; NativeLoaderNamespace* mAppNamespace = nullptr; }; } // namespace android -/* FIXME - * Export an un-mangled function that just does - * return android::GraphicsEnv::getInstance().getDriverNamespace(); - * This allows libEGL to get the function pointer via dlsym, since it can't - * directly link against libgui. In a future release, we'll fix this so that - * libgui does not depend on graphics API libraries, and libEGL can link - * against it. The current dependencies from libgui -> libEGL are: - * - the GLConsumer class, which should be moved to its own library - * - the EGLsyncKHR synchronization in BufferQueue, which is deprecated and - * will be removed soon. - */ -extern "C" android_namespace_t* android_getDriverNamespace(); - #endif // ANDROID_UI_GRAPHICS_ENV_H diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index b29c1d5157..d1c732bc1f 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -58,7 +58,7 @@ cc_library_shared { "-Wno-weak-vtables", // Allow four-character integer literals - "-Wno-four-char-constants", + "-Wno-four-char-constants", // Allow documentation warnings "-Wno-documentation", @@ -106,6 +106,7 @@ cc_library_shared { "IProducerListener.cpp", "ISurfaceComposer.cpp", "ISurfaceComposerClient.cpp", + "ITransactionCompletedListener.cpp", "LayerDebugInfo.cpp", "LayerState.cpp", "OccupancyTracker.cpp", @@ -116,14 +117,16 @@ cc_library_shared { "SyncFeatures.cpp", "view/Surface.cpp", "bufferqueue/1.0/B2HProducerListener.cpp", - "bufferqueue/1.0/H2BGraphicBufferProducer.cpp" + "bufferqueue/1.0/H2BGraphicBufferProducer.cpp", ], shared_libs: [ "android.hardware.graphics.common@1.1", + "libbase", "libsync", "libbinder", - "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. + "libbufferhub", + "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. "libpdx_default_transport", "libcutils", "libEGL", @@ -132,6 +135,7 @@ cc_library_shared { "libutils", "libnativewindow", "liblog", + "libinput", "libhidlbase", "libhidltransport", "android.hidl.token@1.0-utils", @@ -143,14 +147,16 @@ cc_library_shared { // bufferhub is not used when building libgui for vendors target: { vendor: { - cflags: ["-DNO_BUFFERHUB"], + cflags: ["-DNO_BUFFERHUB", "-DNO_INPUT"], exclude_srcs: [ "BufferHubConsumer.cpp", "BufferHubProducer.cpp", ], exclude_shared_libs: [ + "libbufferhub", "libbufferhubqueue", "libpdx_default_transport", + "libinput" ], }, }, diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp index ae5cca2d20..16952a6625 100644 --- a/libs/gui/BufferHubProducer.cpp +++ b/libs/gui/BufferHubProducer.cpp @@ -19,6 +19,7 @@ #include <inttypes.h> #include <log/log.h> #include <system/window.h> +#include <ui/BufferHubBuffer.h> namespace android { @@ -224,23 +225,172 @@ status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp<Fence>* out_fence, u return ret; } -status_t BufferHubProducer::detachBuffer(int /* slot */) { - ALOGE("BufferHubProducer::detachBuffer not implemented."); - return INVALID_OPERATION; +status_t BufferHubProducer::detachBuffer(int slot) { + ALOGV("detachBuffer: slot=%d", slot); + std::unique_lock<std::mutex> lock(mutex_); + + return DetachBufferLocked(static_cast<size_t>(slot)); } -status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* /* out_buffer */, - sp<Fence>* /* out_fence */) { - ALOGE("BufferHubProducer::detachNextBuffer not implemented."); +status_t BufferHubProducer::DetachBufferLocked(size_t slot) { + if (connected_api_ == kNoConnectedApi) { + ALOGE("detachBuffer: BufferHubProducer is not connected."); + return NO_INIT; + } + + if (slot >= static_cast<size_t>(max_buffer_count_)) { + ALOGE("detachBuffer: slot index %zu out of range [0, %d)", slot, max_buffer_count_); + return BAD_VALUE; + } else if (!buffers_[slot].mBufferState.isDequeued()) { + ALOGE("detachBuffer: slot %zu is not owned by the producer (state = %s)", slot, + buffers_[slot].mBufferState.string()); + return BAD_VALUE; + } else if (!buffers_[slot].mRequestBufferCalled) { + ALOGE("detachBuffer: buffer in slot %zu has not been requested", slot); + return BAD_VALUE; + } + std::shared_ptr<BufferProducer> buffer_producer = queue_->GetBuffer(slot); + if (buffer_producer == nullptr || buffer_producer->buffer() == nullptr) { + ALOGE("detachBuffer: Invalid BufferProducer at slot %zu.", slot); + return BAD_VALUE; + } + sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer(); + if (graphic_buffer == nullptr) { + ALOGE("detachBuffer: Invalid GraphicBuffer at slot %zu.", slot); + return BAD_VALUE; + } + + // Remove the BufferProducer from the ProducerQueue. + status_t error = RemoveBuffer(slot); + if (error != NO_ERROR) { + ALOGE("detachBuffer: Failed to remove buffer, slot=%zu, error=%d.", slot, error); + return error; + } + + // Here we need to convert the existing ProducerBuffer into a DetachedBufferHandle and inject + // the handle into the GraphicBuffer object at the requested slot. + auto status_or_handle = buffer_producer->Detach(); + if (!status_or_handle.ok()) { + ALOGE("detachBuffer: Failed to detach from a BufferProducer at slot %zu, error=%d.", slot, + status_or_handle.error()); + return BAD_VALUE; + } + + // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can + // be directly backed by BufferHub. return INVALID_OPERATION; } -status_t BufferHubProducer::attachBuffer(int* /* out_slot */, - const sp<GraphicBuffer>& /* buffer */) { - // With this BufferHub backed implementation, we assume (for now) all buffers - // are allocated and owned by the BufferHub. Thus the attempt of transfering - // ownership of a buffer to the buffer queue is intentionally unsupported. - LOG_ALWAYS_FATAL("BufferHubProducer::attachBuffer not supported."); +status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) { + ALOGV("detachNextBuffer."); + + if (out_buffer == nullptr || out_fence == nullptr) { + ALOGE("detachNextBuffer: Invalid parameter: out_buffer=%p, out_fence=%p", out_buffer, + out_fence); + return BAD_VALUE; + } + + std::unique_lock<std::mutex> lock(mutex_); + + if (connected_api_ == kNoConnectedApi) { + ALOGE("detachNextBuffer: BufferHubProducer is not connected."); + return NO_INIT; + } + + // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer, and detachBuffer in + // sequence, except for two things: + // + // 1) It is unnecessary to know the dimensions, format, or usage of the next buffer, i.e. the + // function just returns whatever BufferProducer is available from the ProducerQueue and no + // buffer allocation or re-allocation will happen. + // 2) It will not block, since if it cannot find an appropriate buffer to return, it will return + // an error instead. + size_t slot = 0; + LocalHandle fence; + + // First, dequeue a BufferProducer from the ProducerQueue with no timeout. Report error + // immediately if ProducerQueue::Dequeue() fails. + auto status_or_buffer = queue_->Dequeue(/*timeout=*/0, &slot, &fence); + if (!status_or_buffer.ok()) { + ALOGE("detachNextBuffer: Failed to dequeue buffer, error=%d.", status_or_buffer.error()); + return NO_MEMORY; + } + + std::shared_ptr<BufferProducer> buffer_producer = status_or_buffer.take(); + if (buffer_producer == nullptr) { + ALOGE("detachNextBuffer: Dequeued buffer is null."); + return NO_MEMORY; + } + + // With the BufferHub backed solution, slot returned from |queue_->Dequeue| is guaranteed to + // be available for producer's use. It's either in free state (if the buffer has never been used + // before) or in queued state (if the buffer has been dequeued and queued back to + // BufferHubQueue). + if (!buffers_[slot].mBufferState.isFree() && !buffers_[slot].mBufferState.isQueued()) { + ALOGE("detachNextBuffer: slot %zu is not free or queued, actual state: %s.", slot, + buffers_[slot].mBufferState.string()); + return BAD_VALUE; + } + if (buffers_[slot].mBufferProducer == nullptr) { + ALOGE("detachNextBuffer: BufferProducer at slot %zu is null.", slot); + return BAD_VALUE; + } + if (buffers_[slot].mBufferProducer->id() != buffer_producer->id()) { + ALOGE("detachNextBuffer: BufferProducer at slot %zu has mismatched id, actual: " + "%d, expected: %d.", + slot, buffers_[slot].mBufferProducer->id(), buffer_producer->id()); + return BAD_VALUE; + } + + ALOGV("detachNextBuffer: slot=%zu", slot); + buffers_[slot].mBufferState.freeQueued(); + buffers_[slot].mBufferState.dequeue(); + + // Second, request the buffer. + sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer(); + buffers_[slot].mGraphicBuffer = buffer_producer->buffer()->buffer(); + + // Finally, detach the buffer and then return. + status_t error = DetachBufferLocked(slot); + if (error == NO_ERROR) { + *out_fence = new Fence(fence.Release()); + *out_buffer = graphic_buffer; + } + return error; +} + +status_t BufferHubProducer::attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) { + // In the BufferHub design, all buffers are allocated and owned by the BufferHub. Thus only + // GraphicBuffers that are originated from BufferHub can be attached to a BufferHubProducer. + ALOGV("queueBuffer: buffer=%p", buffer.get()); + + if (out_slot == nullptr) { + ALOGE("attachBuffer: out_slot cannot be NULL."); + return BAD_VALUE; + } + if (buffer == nullptr) { + ALOGE("attachBuffer: invalid GraphicBuffer."); + return BAD_VALUE; + } + + std::unique_lock<std::mutex> lock(mutex_); + + if (connected_api_ == kNoConnectedApi) { + ALOGE("attachBuffer: BufferQueue has no connected producer"); + return NO_INIT; + } + + // Before attaching the buffer, caller is supposed to call + // IGraphicBufferProducer::setGenerationNumber to inform the + // BufferHubProducer the next generation number. + if (buffer->getGenerationNumber() != generation_number_) { + ALOGE("attachBuffer: Mismatched generation number, buffer: %u, queue: %u.", + buffer->getGenerationNumber(), generation_number_); + return BAD_VALUE; + } + + // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can + // be directly backed by BufferHub. return INVALID_OPERATION; } @@ -370,7 +520,7 @@ status_t BufferHubProducer::cancelBuffer(int slot, const sp<Fence>& fence) { } auto buffer_producer = buffers_[slot].mBufferProducer; - queue_->Enqueue(buffer_producer, size_t(slot), 0ULL); + queue_->Enqueue(buffer_producer, size_t(slot), 0U); buffers_[slot].mBufferState.cancel(); buffers_[slot].mFence = fence; ALOGV("cancelBuffer: slot %d", slot); @@ -654,26 +804,28 @@ status_t BufferHubProducer::AllocateBuffer(uint32_t width, uint32_t height, uint status_t BufferHubProducer::RemoveBuffer(size_t slot) { auto status = queue_->RemoveBuffer(slot); if (!status) { - ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer: %s", - status.GetErrorMessage().c_str()); + ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer at slot: %zu, error: %s.", + slot, status.GetErrorMessage().c_str()); return INVALID_OPERATION; } // Reset in memory objects related the the buffer. buffers_[slot].mBufferProducer = nullptr; - buffers_[slot].mGraphicBuffer = nullptr; buffers_[slot].mBufferState.detachProducer(); + buffers_[slot].mFence = Fence::NO_FENCE; + buffers_[slot].mGraphicBuffer = nullptr; + buffers_[slot].mRequestBufferCalled = false; return NO_ERROR; } status_t BufferHubProducer::FreeAllBuffers() { for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { // Reset in memory objects related the the buffer. - buffers_[slot].mGraphicBuffer = nullptr; - buffers_[slot].mBufferState.reset(); - buffers_[slot].mRequestBufferCalled = false; buffers_[slot].mBufferProducer = nullptr; + buffers_[slot].mBufferState.reset(); buffers_[slot].mFence = Fence::NO_FENCE; + buffers_[slot].mGraphicBuffer = nullptr; + buffers_[slot].mRequestBufferCalled = false; } auto status = queue_->FreeAllBuffers(); diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp index f50379b3ed..5beba02e63 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp @@ -39,8 +39,8 @@ static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { } BufferItem::BufferItem() : - mGraphicBuffer(NULL), - mFence(NULL), + mGraphicBuffer(nullptr), + mFence(nullptr), mCrop(Rect::INVALID_RECT), mTransform(0), mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), @@ -91,11 +91,11 @@ size_t BufferItem::getPodSize() const { size_t BufferItem::getFlattenedSize() const { size_t size = sizeof(uint32_t); // Flags - if (mGraphicBuffer != 0) { + if (mGraphicBuffer != nullptr) { size += mGraphicBuffer->getFlattenedSize(); size = FlattenableUtils::align<4>(size); } - if (mFence != 0) { + if (mFence != nullptr) { size += mFence->getFlattenedSize(); size = FlattenableUtils::align<4>(size); } @@ -107,10 +107,10 @@ size_t BufferItem::getFlattenedSize() const { size_t BufferItem::getFdCount() const { size_t count = 0; - if (mGraphicBuffer != 0) { + if (mGraphicBuffer != nullptr) { count += mGraphicBuffer->getFdCount(); } - if (mFence != 0) { + if (mFence != nullptr) { count += mFence->getFdCount(); } return count; @@ -137,13 +137,13 @@ status_t BufferItem::flatten( FlattenableUtils::advance(buffer, size, sizeof(uint32_t)); flags = 0; - if (mGraphicBuffer != 0) { + if (mGraphicBuffer != nullptr) { status_t err = mGraphicBuffer->flatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); flags |= 1; } - if (mFence != 0) { + if (mFence != nullptr) { status_t err = mFence->flatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index 89bc0c4c2d..f50bc203e8 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -107,7 +107,7 @@ status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, void BufferItemConsumer::freeBufferLocked(int slotIndex) { sp<BufferFreedListener> listener = mBufferFreedListener.promote(); - if (listener != NULL && mSlots[slotIndex].mGraphicBuffer != NULL) { + if (listener != nullptr && mSlots[slotIndex].mGraphicBuffer != nullptr) { // Fire callback if we have a listener registered and the buffer being freed is valid. BI_LOGV("actually calling onBufferFreed"); listener->onBufferFreed(mSlots[slotIndex].mGraphicBuffer); diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index a8da1347cb..5fb3f0b80f 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -38,7 +38,7 @@ BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {} void BufferQueue::ProxyConsumerListener::onDisconnect() { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onDisconnect(); } } @@ -46,7 +46,7 @@ void BufferQueue::ProxyConsumerListener::onDisconnect() { void BufferQueue::ProxyConsumerListener::onFrameAvailable( const BufferItem& item) { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onFrameAvailable(item); } } @@ -54,21 +54,21 @@ void BufferQueue::ProxyConsumerListener::onFrameAvailable( void BufferQueue::ProxyConsumerListener::onFrameReplaced( const BufferItem& item) { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onFrameReplaced(item); } } void BufferQueue::ProxyConsumerListener::onBuffersReleased() { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } } void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onSidebandStreamChanged(); } } @@ -85,21 +85,21 @@ void BufferQueue::ProxyConsumerListener::addAndGetFrameTimestamps( void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer, bool consumerIsSurfaceFlinger) { - LOG_ALWAYS_FATAL_IF(outProducer == NULL, + LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL"); - LOG_ALWAYS_FATAL_IF(outConsumer == NULL, + LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL"); sp<BufferQueueCore> core(new BufferQueueCore()); - LOG_ALWAYS_FATAL_IF(core == NULL, + LOG_ALWAYS_FATAL_IF(core == nullptr, "BufferQueue: failed to create BufferQueueCore"); sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger)); - LOG_ALWAYS_FATAL_IF(producer == NULL, + LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer"); sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); - LOG_ALWAYS_FATAL_IF(consumer == NULL, + LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer"); *outProducer = producer; @@ -109,8 +109,8 @@ void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, #ifndef NO_BUFFERHUB void BufferQueue::createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer) { - LOG_ALWAYS_FATAL_IF(outProducer == NULL, "BufferQueue: outProducer must not be NULL"); - LOG_ALWAYS_FATAL_IF(outConsumer == NULL, "BufferQueue: outConsumer must not be NULL"); + LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL"); + LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL"); sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; @@ -118,16 +118,16 @@ void BufferQueue::createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer, dvr::ProducerQueueConfigBuilder configBuilder; std::shared_ptr<dvr::ProducerQueue> producerQueue = dvr::ProducerQueue::Create(configBuilder.Build(), dvr::UsagePolicy{}); - LOG_ALWAYS_FATAL_IF(producerQueue == NULL, "BufferQueue: failed to create ProducerQueue."); + LOG_ALWAYS_FATAL_IF(producerQueue == nullptr, "BufferQueue: failed to create ProducerQueue."); std::shared_ptr<dvr::ConsumerQueue> consumerQueue = producerQueue->CreateConsumerQueue(); - LOG_ALWAYS_FATAL_IF(consumerQueue == NULL, "BufferQueue: failed to create ConsumerQueue."); + LOG_ALWAYS_FATAL_IF(consumerQueue == nullptr, "BufferQueue: failed to create ConsumerQueue."); producer = BufferHubProducer::Create(producerQueue); consumer = BufferHubConsumer::Create(consumerQueue); - LOG_ALWAYS_FATAL_IF(producer == NULL, "BufferQueue: failed to create BufferQueueProducer"); - LOG_ALWAYS_FATAL_IF(consumer == NULL, "BufferQueue: failed to create BufferQueueConsumer"); + LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer"); + LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer"); *outProducer = producer; *outConsumer = consumer; diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index d70e1422b0..3837c3e11a 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -255,7 +255,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, // mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer // on the consumer side if (outBuffer->mAcquireCalled) { - outBuffer->mGraphicBuffer = NULL; + outBuffer->mGraphicBuffer = nullptr; } mCore->mQueue.erase(front); @@ -272,7 +272,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, VALIDATE_CONSISTENCY(); } - if (listener != NULL) { + if (listener != nullptr) { for (int i = 0; i < numDroppedBuffers; ++i) { listener->onBufferReleased(); } @@ -321,10 +321,10 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot, const sp<android::GraphicBuffer>& buffer) { ATRACE_CALL(); - if (outSlot == NULL) { + if (outSlot == nullptr) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; - } else if (buffer == NULL) { + } else if (buffer == nullptr) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } @@ -413,7 +413,7 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, ATRACE_BUFFER_INDEX(slot); if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS || - releaseFence == NULL) { + releaseFence == nullptr) { BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot, releaseFence.get()); return BAD_VALUE; @@ -465,7 +465,7 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBufferReleased(); } @@ -476,7 +476,7 @@ status_t BufferQueueConsumer::connect( const sp<IConsumerListener>& consumerListener, bool controlledByApp) { ATRACE_CALL(); - if (consumerListener == NULL) { + if (consumerListener == nullptr) { BQ_LOGE("connect: consumerListener may not be NULL"); return BAD_VALUE; } @@ -504,13 +504,13 @@ status_t BufferQueueConsumer::disconnect() { Mutex::Autolock lock(mCore->mMutex); - if (mCore->mConsumerListener == NULL) { + if (mCore->mConsumerListener == nullptr) { BQ_LOGE("disconnect: no consumer is connected"); return BAD_VALUE; } mCore->mIsAbandoned = true; - mCore->mConsumerListener = NULL; + mCore->mConsumerListener = nullptr; mCore->mQueue.clear(); mCore->freeAllBuffersLocked(); mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; @@ -521,7 +521,7 @@ status_t BufferQueueConsumer::disconnect() { status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) { ATRACE_CALL(); - if (outSlotMask == NULL) { + if (outSlotMask == nullptr) { BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL"); return BAD_VALUE; } @@ -673,7 +673,7 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( } } // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -772,7 +772,7 @@ status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResul if (uid != shellUid) { #endif android_errorWriteWithInfoLog(0x534e4554, "27046057", - static_cast<int32_t>(uid), NULL, 0); + static_cast<int32_t>(uid), nullptr, 0); return PERMISSION_DENIED; } diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index bb703da3dd..960b1948c2 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -349,7 +349,7 @@ void BufferQueueCore::validateConsistencyLocked() const { BQ_LOGE("Slot %d is in mUnusedSlots but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer != NULL) { + if (mSlots[slot].mGraphicBuffer != nullptr) { BQ_LOGE("Slot %d is in mUnusedSluts but has an active buffer", slot); usleep(PAUSE_TIME); @@ -371,7 +371,7 @@ void BufferQueueCore::validateConsistencyLocked() const { BQ_LOGE("Slot %d is in mFreeSlots but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer != NULL) { + if (mSlots[slot].mGraphicBuffer != nullptr) { BQ_LOGE("Slot %d is in mFreeSlots but has a buffer", slot); usleep(PAUSE_TIME); @@ -394,7 +394,7 @@ void BufferQueueCore::validateConsistencyLocked() const { BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer == NULL) { + if (mSlots[slot].mGraphicBuffer == nullptr) { BQ_LOGE("Slot %d is in mFreeBuffers but has no buffer", slot); usleep(PAUSE_TIME); } @@ -418,7 +418,7 @@ void BufferQueueCore::validateConsistencyLocked() const { BQ_LOGE("Slot %d is in mActiveBuffers but is FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer == NULL && !mIsAllocating) { + if (mSlots[slot].mGraphicBuffer == nullptr && !mIsAllocating) { BQ_LOGE("Slot %d is in mActiveBuffers but has no buffer", slot); usleep(PAUSE_TIME); } diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index c96a2dd6a3..5e250a4185 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -166,7 +166,7 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount( } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -221,7 +221,7 @@ status_t BufferQueueProducer::setAsyncMode(bool async) { } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; @@ -449,11 +449,11 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou mSlots[found].mBufferState.dequeue(); - if ((buffer == NULL) || + if ((buffer == nullptr) || buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { mSlots[found].mAcquireCalled = false; - mSlots[found].mGraphicBuffer = NULL; + mSlots[found].mGraphicBuffer = nullptr; mSlots[found].mRequestBufferCalled = false; mSlots[found].mEglDisplay = EGL_NO_DISPLAY; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; @@ -471,7 +471,7 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64, mCore->mBufferAge); - if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { + if (CC_UNLIKELY(mSlots[found].mFence == nullptr)) { BQ_LOGE("dequeueBuffer: about to return a NULL fence - " "slot=%d w=%d h=%d format=%u", found, buffer->width, buffer->height, buffer->format); @@ -612,7 +612,7 @@ status_t BufferQueueProducer::detachBuffer(int slot) { listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -623,10 +623,10 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { ATRACE_CALL(); - if (outBuffer == NULL) { + if (outBuffer == nullptr) { BQ_LOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; - } else if (outFence == NULL) { + } else if (outFence == nullptr) { BQ_LOGE("detachNextBuffer: outFence must not be NULL"); return BAD_VALUE; } @@ -670,7 +670,7 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer, listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -681,10 +681,10 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot, const sp<android::GraphicBuffer>& buffer) { ATRACE_CALL(); - if (outSlot == NULL) { + if (outSlot == nullptr) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; - } else if (buffer == NULL) { + } else if (buffer == nullptr) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } @@ -766,7 +766,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, const Region& surfaceDamage = input.getSurfaceDamage(); const HdrMetadata& hdrMetadata = input.getHdrMetadata(); - if (acquireFence == NULL) { + if (acquireFence == nullptr) { BQ_LOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } @@ -972,9 +972,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, mCallbackCondition.wait(mCallbackMutex); } - if (frameAvailableListener != NULL) { + if (frameAvailableListener != nullptr) { frameAvailableListener->onFrameAvailable(item); - } else if (frameReplacedListener != NULL) { + } else if (frameReplacedListener != nullptr) { frameReplacedListener->onFrameReplaced(item); } @@ -1039,7 +1039,7 @@ status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) { BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; - } else if (fence == NULL) { + } else if (fence == nullptr) { BQ_LOGE("cancelBuffer: fence is NULL"); return BAD_VALUE; } @@ -1069,7 +1069,7 @@ int BufferQueueProducer::query(int what, int *outValue) { ATRACE_CALL(); Mutex::Autolock lock(mCore->mMutex); - if (outValue == NULL) { + if (outValue == nullptr) { BQ_LOGE("query: outValue was NULL"); return BAD_VALUE; } @@ -1145,12 +1145,12 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, return NO_INIT; } - if (mCore->mConsumerListener == NULL) { + if (mCore->mConsumerListener == nullptr) { BQ_LOGE("connect: BufferQueue has no consumer"); return NO_INIT; } - if (output == NULL) { + if (output == nullptr) { BQ_LOGE("connect: output was NULL"); return BAD_VALUE; } @@ -1188,10 +1188,10 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, output->nextFrameNumber = mCore->mFrameCounter + 1; output->bufferReplaced = false; - if (listener != NULL) { + if (listener != nullptr) { // Set up a death notification so that we can disconnect // automatically if the remote producer dies - if (IInterface::asBinder(listener)->remoteBinder() != NULL) { + if (IInterface::asBinder(listener)->remoteBinder() != nullptr) { status = IInterface::asBinder(listener)->linkToDeath( static_cast<IBinder::DeathRecipient*>(this)); if (status != NO_ERROR) { @@ -1268,7 +1268,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { mCore->freeAllBuffersLocked(); // Remove our death notification callback if we have one - if (mCore->mLinkedToDeath != NULL) { + if (mCore->mLinkedToDeath != nullptr) { sp<IBinder> token = IInterface::asBinder(mCore->mLinkedToDeath); // This can fail if we're here because of the death @@ -1278,8 +1278,8 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { } mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; - mCore->mLinkedToDeath = NULL; - mCore->mConnectedProducerListener = NULL; + mCore->mLinkedToDeath = nullptr; + mCore->mConnectedProducerListener = nullptr; mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; mCore->mConnectedPid = -1; mCore->mSidebandStream.clear(); @@ -1302,7 +1302,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); listener->onDisconnect(); } @@ -1318,7 +1318,7 @@ status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) listener = mCore->mConsumerListener; } // Autolock scope - if (listener != NULL) { + if (listener != nullptr) { listener->onSidebandStreamChanged(); } return NO_ERROR; @@ -1536,7 +1536,7 @@ void BufferQueueProducer::addAndGetFrameTimestamps( Mutex::Autolock lock(mCore->mMutex); listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->addAndGetFrameTimestamps(newTimestamps, outDelta); } } diff --git a/libs/gui/CleanSpec.mk b/libs/gui/CleanSpec.mk deleted file mode 100644 index 5a5144c9df..0000000000 --- a/libs/gui/CleanSpec.mk +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (C) 2012 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. -# - -# If you don't need to do a full clean build but would like to touch -# a file or delete some intermediate files, add a clean step to the end -# of the list. These steps will only be run once, if they haven't been -# run before. -# -# E.g.: -# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) -# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) -# -# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with -# files that are missing or have been moved. -# -# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. -# Use $(OUT_DIR) to refer to the "out" directory. -# -# If you need to re-do something that's already mentioned, just copy -# the command and add it to the bottom of the list. E.g., if a change -# that you made last week required touching a file and a change you -# made today requires touching the same file, just copy the old -# touch step and add it to the end of the list. -# -# ************************************************ -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST -# ************************************************ - -# For example: -#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) -#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) -#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) -#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) - -# ************************************************ -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST -# ************************************************ -$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libgui*" -print0 | xargs -0 rm -f) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates) diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index f9e292e199..abd9921fa9 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -96,7 +96,7 @@ void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) { void ConsumerBase::freeBufferLocked(int slotIndex) { CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); - mSlots[slotIndex].mGraphicBuffer = 0; + mSlots[slotIndex].mGraphicBuffer = nullptr; mSlots[slotIndex].mFence = Fence::NO_FENCE; mSlots[slotIndex].mFrameNumber = 0; } @@ -110,7 +110,7 @@ void ConsumerBase::onFrameAvailable(const BufferItem& item) { listener = mFrameAvailableListener.promote(); } - if (listener != NULL) { + if (listener != nullptr) { CB_LOGV("actually calling onFrameAvailable"); listener->onFrameAvailable(item); } @@ -125,7 +125,7 @@ void ConsumerBase::onFrameReplaced(const BufferItem &item) { listener = mFrameAvailableListener.promote(); } - if (listener != NULL) { + if (listener != nullptr) { CB_LOGV("actually calling onFrameReplaced"); listener->onFrameReplaced(item); } @@ -352,8 +352,8 @@ status_t ConsumerBase::acquireBufferLocked(BufferItem *item, return err; } - if (item->mGraphicBuffer != NULL) { - if (mSlots[item->mSlot].mGraphicBuffer != NULL) { + if (item->mGraphicBuffer != nullptr) { + if (mSlots[item->mSlot].mGraphicBuffer != nullptr) { freeBufferLocked(item->mSlot); } mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer; @@ -468,7 +468,7 @@ bool ConsumerBase::stillTracking(int slot, if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) { return false; } - return (mSlots[slot].mGraphicBuffer != NULL && + return (mSlots[slot].mGraphicBuffer != nullptr && mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle); } diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index 1757ec1cd3..f5cf1c4d5a 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -34,9 +34,9 @@ namespace android { DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - if (sf != NULL) { + if (sf != nullptr) { mEventConnection = sf->createDisplayEventConnection(vsyncSource); - if (mEventConnection != NULL) { + if (mEventConnection != nullptr) { mDataChannel = std::make_unique<gui::BitTube>(); mEventConnection->stealReceiveChannel(mDataChannel.get()); } @@ -47,13 +47,13 @@ DisplayEventReceiver::~DisplayEventReceiver() { } status_t DisplayEventReceiver::initCheck() const { - if (mDataChannel != NULL) + if (mDataChannel != nullptr) return NO_ERROR; return NO_INIT; } int DisplayEventReceiver::getFd() const { - if (mDataChannel == NULL) + if (mDataChannel == nullptr) return NO_INIT; return mDataChannel->getFd(); @@ -63,7 +63,7 @@ status_t DisplayEventReceiver::setVsyncRate(uint32_t count) { if (int32_t(count) < 0) return BAD_VALUE; - if (mEventConnection != NULL) { + if (mEventConnection != nullptr) { mEventConnection->setVsyncRate(count); return NO_ERROR; } @@ -71,7 +71,7 @@ status_t DisplayEventReceiver::setVsyncRate(uint32_t count) { } status_t DisplayEventReceiver::requestNextVsync() { - if (mEventConnection != NULL) { + if (mEventConnection != nullptr) { mEventConnection->requestNextVsync(); return NO_ERROR; } diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp index 85ae433e42..96e9a85fd2 100644 --- a/libs/gui/FrameTimestamps.cpp +++ b/libs/gui/FrameTimestamps.cpp @@ -18,10 +18,10 @@ #define LOG_TAG "FrameEvents" +#include <android-base/stringprintf.h> #include <cutils/compiler.h> // For CC_[UN]LIKELY #include <inttypes.h> #include <utils/Log.h> -#include <utils/String8.h> #include <algorithm> #include <limits> @@ -29,6 +29,7 @@ namespace android { +using base::StringAppendF; // ============================================================================ // FrameEvents @@ -86,50 +87,49 @@ void FrameEvents::checkFencesForCompletion() { releaseFence->getSignalTime(); } -static void dumpFenceTime(String8& outString, const char* name, - bool pending, const FenceTime& fenceTime) { - outString.appendFormat("--- %s", name); +static void dumpFenceTime(std::string& outString, const char* name, bool pending, + const FenceTime& fenceTime) { + StringAppendF(&outString, "--- %s", name); nsecs_t signalTime = fenceTime.getCachedSignalTime(); if (Fence::isValidTimestamp(signalTime)) { - outString.appendFormat("%" PRId64 "\n", signalTime); + StringAppendF(&outString, "%" PRId64 "\n", signalTime); } else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) { - outString.appendFormat("Pending\n"); + outString.append("Pending\n"); } else if (&fenceTime == FenceTime::NO_FENCE.get()){ - outString.appendFormat("N/A\n"); + outString.append("N/A\n"); } else { - outString.appendFormat("Error\n"); + outString.append("Error\n"); } } -void FrameEvents::dump(String8& outString) const -{ +void FrameEvents::dump(std::string& outString) const { if (!valid) { return; } - outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber); - outString.appendFormat("--- Posted \t%" PRId64 "\n", postedTime); - outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime); + StringAppendF(&outString, "-- Frame %" PRIu64 "\n", frameNumber); + StringAppendF(&outString, "--- Posted \t%" PRId64 "\n", postedTime); + StringAppendF(&outString, "--- Req. Present\t%" PRId64 "\n", requestedPresentTime); - outString.appendFormat("--- Latched \t"); + outString.append("--- Latched \t"); if (FrameEvents::isValidTimestamp(latchTime)) { - outString.appendFormat("%" PRId64 "\n", latchTime); + StringAppendF(&outString, "%" PRId64 "\n", latchTime); } else { - outString.appendFormat("Pending\n"); + outString.append("Pending\n"); } - outString.appendFormat("--- Refresh (First)\t"); + outString.append("--- Refresh (First)\t"); if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) { - outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime); + StringAppendF(&outString, "%" PRId64 "\n", firstRefreshStartTime); } else { - outString.appendFormat("Pending\n"); + outString.append("Pending\n"); } - outString.appendFormat("--- Refresh (Last)\t"); + outString.append("--- Refresh (Last)\t"); if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) { - outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime); + StringAppendF(&outString, "%" PRId64 "\n", lastRefreshStartTime); } else { - outString.appendFormat("Pending\n"); + outString.append("Pending\n"); } dumpFenceTime(outString, "Acquire \t", @@ -139,11 +139,11 @@ void FrameEvents::dump(String8& outString) const dumpFenceTime(outString, "Display Present \t", !addPostCompositeCalled, *displayPresentFence); - outString.appendFormat("--- DequeueReady \t"); + outString.append("--- DequeueReady \t"); if (FrameEvents::isValidTimestamp(dequeueReadyTime)) { - outString.appendFormat("%" PRId64 "\n", dequeueReadyTime); + StringAppendF(&outString, "%" PRId64 "\n", dequeueReadyTime); } else { - outString.appendFormat("Pending\n"); + outString.append("Pending\n"); } dumpFenceTime(outString, "Release \t", @@ -206,11 +206,11 @@ static bool FrameNumberLessThan( return lhs.valid; } -void FrameEventHistory::dump(String8& outString) const { +void FrameEventHistory::dump(std::string& outString) const { auto earliestFrame = std::min_element( mFrames.begin(), mFrames.end(), &FrameNumberLessThan); if (!earliestFrame->valid) { - outString.appendFormat("-- N/A\n"); + outString.append("-- N/A\n"); return; } for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) { diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 885efec9b9..faf02f3b53 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -46,7 +46,6 @@ #include <utils/Trace.h> extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); -#define CROP_EXT_STR "EGL_ANDROID_image_crop" #define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" #define EGL_PROTECTED_CONTENT_EXT 0x32C0 @@ -82,26 +81,6 @@ static const mat4 mtxIdentity; Mutex GLConsumer::sStaticInitLock; sp<GraphicBuffer> GLConsumer::sReleasedTexImageBuffer; -static bool hasEglAndroidImageCropImpl() { - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - size_t cropExtLen = strlen(CROP_EXT_STR); - size_t extsLen = strlen(exts); - bool equal = !strcmp(CROP_EXT_STR, exts); - bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1); - bool atEnd = (cropExtLen+1) < extsLen && - !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1)); - bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); - return equal || atStart || atEnd || inMiddle; -} - -static bool hasEglAndroidImageCrop() { - // Only compute whether the extension is present once the first time this - // function is called. - static bool hasIt = hasEglAndroidImageCropImpl(); - return hasIt; -} - static bool hasEglProtectedContentImpl() { EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); @@ -122,10 +101,6 @@ static bool hasEglProtectedContent() { return hasIt; } -static bool isEglImageCroppable(const Rect& crop) { - return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0); -} - GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : ConsumerBase(bq, isControlledByApp), @@ -291,7 +266,7 @@ status_t GLConsumer::releaseTexImage() { return err; } - if (mReleasedTexImage == NULL) { + if (mReleasedTexImage == nullptr) { mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); } @@ -321,7 +296,7 @@ status_t GLConsumer::releaseTexImage() { sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() { Mutex::Autolock _l(sStaticInitLock); - if (CC_UNLIKELY(sReleasedTexImageBuffer == NULL)) { + if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { // The first time, create the debug texture in case the application // continues to use it. sp<GraphicBuffer> buffer = new GraphicBuffer( @@ -357,7 +332,7 @@ status_t GLConsumer::acquireBufferLocked(BufferItem *item, // If item->mGraphicBuffer is not null, this buffer has not been acquired // before, so any prior EglImage created is using a stale buffer. This // replaces any old EglImage with a new one (using the new buffer). - if (item->mGraphicBuffer != NULL) { + if (item->mGraphicBuffer != nullptr) { int slot = item->mSlot; mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer); } @@ -406,7 +381,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, // ConsumerBase. // We may have to do this even when item.mGraphicBuffer == NULL (which // means the buffer was previously acquired). - err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay, item.mCrop); + err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); if (err != NO_ERROR) { GLC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, slot); @@ -430,8 +405,8 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, } GLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", - mCurrentTexture, mCurrentTextureImage != NULL ? - mCurrentTextureImage->graphicBufferHandle() : 0, + mCurrentTexture, mCurrentTextureImage != nullptr ? + mCurrentTextureImage->graphicBufferHandle() : nullptr, slot, mSlots[slot].mGraphicBuffer->handle); // Hang onto the pointer so that it isn't freed in the call to @@ -491,13 +466,12 @@ status_t GLConsumer::bindTextureImageLocked() { glBindTexture(mTexTarget, mTexName); if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && - mCurrentTextureImage == NULL) { + mCurrentTextureImage == nullptr) { GLC_LOGE("bindTextureImage: no currently-bound texture"); return NO_INIT; } - status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay, - mCurrentCrop); + status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); if (err != NO_ERROR) { GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, mCurrentTexture); @@ -511,9 +485,7 @@ status_t GLConsumer::bindTextureImageLocked() { // forcing the creation of a new image. if ((error = glGetError()) != GL_NO_ERROR) { glBindTexture(mTexTarget, mTexName); - status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, - mCurrentCrop, - true); + status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); if (result != NO_ERROR) { GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, mCurrentTexture); @@ -655,7 +627,7 @@ status_t GLConsumer::attachToContext(uint32_t tex) { mTexName = tex; mAttached = true; - if (mCurrentTextureImage != NULL) { + if (mCurrentTextureImage != nullptr) { // This may wait for a buffer a second time. This is likely required if // this is a different context, since otherwise the wait could be skipped // by bouncing through another context. For the same context the extra @@ -676,7 +648,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (SyncFeatures::getInstance().useNativeFenceSync()) { EGLSyncKHR sync = eglCreateSyncKHR(dpy, - EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); + EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); if (sync == EGL_NO_SYNC_KHR) { GLC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); @@ -720,7 +692,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { // Create a fence for the outstanding accesses in the current // OpenGL ES context. - fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); + fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); if (fence == EGL_NO_SYNC_KHR) { GLC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); @@ -752,11 +724,11 @@ void GLConsumer::setFilteringEnabled(bool enabled) { bool needsRecompute = mFilteringEnabled != enabled; mFilteringEnabled = enabled; - if (needsRecompute && mCurrentTextureImage==NULL) { + if (needsRecompute && mCurrentTextureImage==nullptr) { GLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL"); } - if (needsRecompute && mCurrentTextureImage != NULL) { + if (needsRecompute && mCurrentTextureImage != nullptr) { computeCurrentTransformMatrixLocked(); } } @@ -769,8 +741,7 @@ void GLConsumer::computeCurrentTransformMatrixLocked() { GLC_LOGD("computeCurrentTransformMatrixLocked: " "mCurrentTextureImage is NULL"); } - computeTransformMatrix(mCurrentTransformMatrix, buf, - isEglImageCroppable(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop, + computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, mFilteringEnabled); } @@ -863,10 +834,10 @@ void GLConsumer::computeTransformMatrix(float outTransform[16], xform = crop * xform; } - // SurfaceFlinger expects the top of its window textures to be at a Y - // coordinate of 0, so GLConsumer must behave the same way. We don't - // want to expose this to applications, however, so we must add an - // additional vertical flip to the transform after all the other transforms. + // GLConsumer uses the GL convention where (0, 0) is the bottom-left + // corner and (1, 1) is the top-right corner. Add an additional vertical + // flip after all other transforms to map from GL convention to buffer + // queue memory layout, where (0, 0) is the top-left corner. xform = mtxFlipV * xform; memcpy(outTransform, xform.asArray(), sizeof(xform)); @@ -938,7 +909,7 @@ sp<GraphicBuffer> GLConsumer::getCurrentBuffer(int* outSlot) const { } return (mCurrentTextureImage == nullptr) ? - NULL : mCurrentTextureImage->graphicBuffer(); + nullptr : mCurrentTextureImage->graphicBuffer(); } Rect GLConsumer::getCurrentCrop() const { @@ -1063,8 +1034,7 @@ void GLConsumer::dumpLocked(String8& result, const char* prefix) const GLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), - mEglDisplay(EGL_NO_DISPLAY), - mCropRect(Rect::EMPTY_RECT) { + mEglDisplay(EGL_NO_DISPLAY) { } GLConsumer::EglImage::~EglImage() { @@ -1077,13 +1047,11 @@ GLConsumer::EglImage::~EglImage() { } status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, - const Rect& cropRect, bool forceCreation) { // If there's an image and it's no longer valid, destroy it. bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; bool displayInvalid = mEglDisplay != eglDisplay; - bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect; - if (haveImage && (displayInvalid || cropInvalid || forceCreation)) { + if (haveImage && (displayInvalid || forceCreation)) { if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("createIfNeeded: eglDestroyImageKHR failed"); } @@ -1095,14 +1063,12 @@ status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, // If there's no image, create one. if (mEglImage == EGL_NO_IMAGE_KHR) { mEglDisplay = eglDisplay; - mCropRect = cropRect; - mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect); + mEglImage = createImage(mEglDisplay, mGraphicBuffer); } // Fail if we can't create a valid image. if (mEglImage == EGL_NO_IMAGE_KHR) { mEglDisplay = EGL_NO_DISPLAY; - mCropRect.makeInvalid(); const sp<GraphicBuffer>& buffer = mGraphicBuffer; ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", buffer->getWidth(), buffer->getHeight(), buffer->getStride(), @@ -1119,38 +1085,19 @@ void GLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { } EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) { + const sp<GraphicBuffer>& graphicBuffer) { EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer()); const bool createProtectedImage = (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, - EGL_IMAGE_CROP_LEFT_ANDROID, crop.left, - EGL_IMAGE_CROP_TOP_ANDROID, crop.top, - EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right, - EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom, + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, createProtectedImage ? EGL_TRUE : EGL_NONE, EGL_NONE, }; - if (!crop.isValid()) { - // No crop rect to set, so leave the crop out of the attrib array. Make - // sure to propagate the protected content attrs if they are set. - attrs[2] = attrs[10]; - attrs[3] = attrs[11]; - attrs[4] = EGL_NONE; - } else if (!isEglImageCroppable(crop)) { - // The crop rect is not at the origin, so we can't set the crop on the - // EGLImage because that's not allowed by the EGL_ANDROID_image_crop - // extension. In the future we can add a layered extension that - // removes this restriction if there is hardware that can support it. - attrs[2] = attrs[10]; - attrs[3] = attrs[11]; - attrs[4] = EGL_NONE; - } - eglInitialize(dpy, 0, 0); + eglInitialize(dpy, nullptr, nullptr); EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); if (image == EGL_NO_IMAGE_KHR) { diff --git a/libs/gui/GuiConfig.cpp b/libs/gui/GuiConfig.cpp index bc0c83c557..3ec20ee0c0 100644 --- a/libs/gui/GuiConfig.cpp +++ b/libs/gui/GuiConfig.cpp @@ -18,8 +18,7 @@ namespace android { -void appendGuiConfigString(String8& configStr) -{ +void appendGuiConfigString(std::string& configStr) { static const char* config = " [libgui" #ifdef DONT_USE_FENCE_SYNC diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp index b715e431d5..add3ef0458 100644 --- a/libs/gui/HdrMetadata.cpp +++ b/libs/gui/HdrMetadata.cpp @@ -15,6 +15,7 @@ */ #include <gui/HdrMetadata.h> +#include <limits> namespace android { @@ -26,6 +27,10 @@ size_t HdrMetadata::getFlattenedSize() const { if (validTypes & CTA861_3) { size += sizeof(cta8613); } + if (validTypes & HDR10PLUS) { + size += sizeof(size_t); + size += hdr10plus.size(); + } return size; } @@ -41,6 +46,12 @@ status_t HdrMetadata::flatten(void* buffer, size_t size) const { if (validTypes & CTA861_3) { FlattenableUtils::write(buffer, size, cta8613); } + if (validTypes & HDR10PLUS) { + size_t metadataSize = hdr10plus.size(); + FlattenableUtils::write(buffer, size, metadataSize); + memcpy(buffer, hdr10plus.data(), metadataSize); + FlattenableUtils::advance(buffer, size, metadataSize); + } return NO_ERROR; } @@ -62,6 +73,22 @@ status_t HdrMetadata::unflatten(void const* buffer, size_t size) { } FlattenableUtils::read(buffer, size, cta8613); } + if (validTypes & HDR10PLUS) { + if (size < sizeof(size_t)) { + return NO_MEMORY; + } + + size_t metadataSize; + FlattenableUtils::read(buffer, size, metadataSize); + + if (size < metadataSize) { + return NO_MEMORY; + } + + hdr10plus.resize(metadataSize); + memcpy(hdr10plus.data(), buffer, metadataSize); + FlattenableUtils::advance(buffer, size, metadataSize); + } return NO_ERROR; } @@ -91,6 +118,10 @@ bool HdrMetadata::operator==(const HdrMetadata& rhs) const { } } + if ((validTypes & HDR10PLUS) == HDR10PLUS) { + if (hdr10plus != rhs.hdr10plus) return false; + } + return true; } diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 0b3796056d..68a6b1fe38 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -190,10 +190,10 @@ public: virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { - if (outBuffer == NULL) { + if (outBuffer == nullptr) { ALOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; - } else if (outFence == NULL) { + } else if (outFence == nullptr) { ALOGE("detachNextBuffer: outFence must not be NULL"); return BAD_VALUE; } @@ -301,7 +301,7 @@ public: int api, bool producerControlledByApp, QueueBufferOutput* output) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); - if (listener != NULL) { + if (listener != nullptr) { data.writeInt32(1); data.writeStrongBinder(IInterface::asBinder(listener)); } else { @@ -738,8 +738,8 @@ status_t BnGraphicBufferProducer::onTransact( int bufferIdx = data.readInt32(); sp<GraphicBuffer> buffer; int result = requestBuffer(bufferIdx, &buffer); - reply->writeInt32(buffer != 0); - if (buffer != 0) { + reply->writeInt32(buffer != nullptr); + if (buffer != nullptr) { reply->write(*buffer); } reply->writeInt32(result); @@ -797,12 +797,12 @@ status_t BnGraphicBufferProducer::onTransact( int32_t result = detachNextBuffer(&buffer, &fence); reply->writeInt32(result); if (result == NO_ERROR) { - reply->writeInt32(buffer != NULL); - if (buffer != NULL) { + reply->writeInt32(buffer != nullptr); + if (buffer != nullptr) { reply->write(*buffer); } - reply->writeInt32(fence != NULL); - if (fence != NULL) { + reply->writeInt32(fence != nullptr); + if (fence != nullptr) { reply->write(*fence); } } diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index d2d27e8239..2d6be26903 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -103,59 +103,64 @@ public: } virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, + const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, ISurfaceComposer::Rotation rotation) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); + data.writeInt32(static_cast<int32_t>(reqDataspace)); + data.writeInt32(static_cast<int32_t>(reqPixelFormat)); data.write(sourceCrop); data.writeUint32(reqWidth); data.writeUint32(reqHeight); - data.writeInt32(minLayerZ); - data.writeInt32(maxLayerZ); data.writeInt32(static_cast<int32_t>(useIdentityTransform)); data.writeInt32(static_cast<int32_t>(rotation)); - status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); - - if (err != NO_ERROR) { - return err; + status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); + if (result != NO_ERROR) { + ALOGE("captureScreen failed to transact: %d", result); + return result; } - - err = reply.readInt32(); - if (err != NO_ERROR) { - return err; + result = reply.readInt32(); + if (result != NO_ERROR) { + ALOGE("captureScreen failed to readInt32: %d", result); + return result; } *outBuffer = new GraphicBuffer(); reply.read(**outBuffer); - return err; + + return result; } virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder, - sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop, + sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, float frameScale, bool childrenOnly) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(layerHandleBinder); + data.writeInt32(static_cast<int32_t>(reqDataspace)); + data.writeInt32(static_cast<int32_t>(reqPixelFormat)); data.write(sourceCrop); data.writeFloat(frameScale); data.writeBool(childrenOnly); - status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); - - if (err != NO_ERROR) { - return err; + status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); + if (result != NO_ERROR) { + ALOGE("captureLayers failed to transact: %d", result); + return result; } - - err = reply.readInt32(); - if (err != NO_ERROR) { - return err; + result = reply.readInt32(); + if (result != NO_ERROR) { + ALOGE("captureLayers failed to readInt32: %d", result); + return result; } *outBuffer = new GraphicBuffer(); reply.read(**outBuffer); - return err; + return result; } virtual bool authenticateSurfaceTexture( @@ -332,50 +337,38 @@ public: return result; } - virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) { + virtual int getActiveConfig(const sp<IBinder>& display) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + remote()->transact(BnSurfaceComposer::GET_ACTIVE_CONFIG, data, &reply); + return reply.readInt32(); + } + + virtual status_t setActiveConfig(const sp<IBinder>& display, int id) + { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to writeInterfaceToken: %d", result); + ALOGE("setActiveConfig failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(display); if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to writeStrongBinder: %d", result); + ALOGE("setActiveConfig failed to writeStrongBinder: %d", result); return result; } - result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_VIEWPORT, data, &reply); + result = data.writeInt32(id); if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to transact: %d", result); + ALOGE("setActiveConfig failed to writeInt32: %d", result); return result; } - result = reply.readInt32(); - if (result == NO_ERROR) { - result = reply.read(*outViewport); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to read: %d", result); - return result; - } + result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply); + if (result != NO_ERROR) { + ALOGE("setActiveConfig failed to transact: %d", result); + return result; } - return result; - } - - virtual int getActiveConfig(const sp<IBinder>& display) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - remote()->transact(BnSurfaceComposer::GET_ACTIVE_CONFIG, data, &reply); - return reply.readInt32(); - } - - virtual status_t setActiveConfig(const sp<IBinder>& display, int id) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - data.writeInt32(id); - remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply); return reply.readInt32(); } @@ -457,8 +450,16 @@ public: virtual status_t clearAnimationFrameStats() { Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply); + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("clearAnimationFrameStats failed to writeInterfaceToken: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply); + if (result != NO_ERROR) { + ALOGE("clearAnimationFrameStats failed to transact: %d", result); + return result; + } return reply.readInt32(); } @@ -563,6 +564,132 @@ public: outLayers->clear(); return reply.readParcelableVector(outLayers); } + + virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace, + ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, + ui::PixelFormat* wideColorGamutPixelFormat) const { + Parcel data, reply; + status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (error != NO_ERROR) { + return error; + } + error = remote()->transact(BnSurfaceComposer::GET_COMPOSITION_PREFERENCE, data, &reply); + if (error != NO_ERROR) { + return error; + } + error = static_cast<status_t>(reply.readInt32()); + if (error == NO_ERROR) { + *defaultDataspace = static_cast<ui::Dataspace>(reply.readInt32()); + *defaultPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32()); + *wideColorGamutDataspace = static_cast<ui::Dataspace>(reply.readInt32()); + *wideColorGamutPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32()); + } + return error; + } + + virtual status_t getColorManagement(bool* outGetColorManagement) const { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::GET_COLOR_MANAGEMENT, data, &reply); + bool result; + status_t err = reply.readBool(&result); + if (err == NO_ERROR) { + *outGetColorManagement = result; + } + return err; + } + + virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display, + ui::PixelFormat* outFormat, + ui::Dataspace* outDataspace, + uint8_t* outComponentMask) const { + if (!outFormat || !outDataspace || !outComponentMask) return BAD_VALUE; + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + + status_t error = + remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES, + data, &reply); + if (error != NO_ERROR) { + return error; + } + + uint32_t value = 0; + error = reply.readUint32(&value); + if (error != NO_ERROR) { + return error; + } + *outFormat = static_cast<ui::PixelFormat>(value); + + error = reply.readUint32(&value); + if (error != NO_ERROR) { + return error; + } + *outDataspace = static_cast<ui::Dataspace>(value); + + error = reply.readUint32(&value); + if (error != NO_ERROR) { + return error; + } + *outComponentMask = static_cast<uint8_t>(value); + return error; + } + + virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, + uint8_t componentMask, + uint64_t maxFrames) const { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + data.writeBool(enable); + data.writeByte(static_cast<int8_t>(componentMask)); + data.writeUint64(maxFrames); + status_t result = + remote()->transact(BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED, data, + &reply); + return result; + } + + virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, + uint64_t timestamp, + DisplayedFrameStats* outStats) const { + if (!outStats) return BAD_VALUE; + + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + data.writeUint64(maxFrames); + data.writeUint64(timestamp); + + status_t result = + remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE, data, &reply); + + if (result != NO_ERROR) { + return result; + } + + result = reply.readUint64(&outStats->numFrames); + if (result != NO_ERROR) { + return result; + } + + result = reply.readUint64Vector(&outStats->component_0_sample); + if (result != NO_ERROR) { + return result; + } + result = reply.readUint64Vector(&outStats->component_1_sample); + if (result != NO_ERROR) { + return result; + } + result = reply.readUint64Vector(&outStats->component_2_sample); + if (result != NO_ERROR) { + return result; + } + result = reply.readUint64Vector(&outStats->component_3_sample); + return result; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -598,10 +725,10 @@ status_t BnSurfaceComposer::onTransact( if (count > data.dataSize()) { return BAD_VALUE; } - ComposerState s; Vector<ComposerState> state; state.setCapacity(count); for (size_t i = 0; i < count; i++) { + ComposerState s; if (s.read(data) == BAD_VALUE) { return BAD_VALUE; } @@ -634,18 +761,18 @@ status_t BnSurfaceComposer::onTransact( case CAPTURE_SCREEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); + ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32()); + ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32()); sp<GraphicBuffer> outBuffer; Rect sourceCrop(Rect::EMPTY_RECT); data.read(sourceCrop); uint32_t reqWidth = data.readUint32(); uint32_t reqHeight = data.readUint32(); - int32_t minLayerZ = data.readInt32(); - int32_t maxLayerZ = data.readInt32(); bool useIdentityTransform = static_cast<bool>(data.readInt32()); int32_t rotation = data.readInt32(); - status_t res = captureScreen(display, &outBuffer, sourceCrop, reqWidth, reqHeight, - minLayerZ, maxLayerZ, useIdentityTransform, + status_t res = captureScreen(display, &outBuffer, reqDataspace, reqPixelFormat, + sourceCrop, reqWidth, reqHeight, useIdentityTransform, static_cast<ISurfaceComposer::Rotation>(rotation)); reply->writeInt32(res); if (res == NO_ERROR) { @@ -656,14 +783,16 @@ status_t BnSurfaceComposer::onTransact( case CAPTURE_LAYERS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> layerHandleBinder = data.readStrongBinder(); + ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32()); + ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32()); sp<GraphicBuffer> outBuffer; Rect sourceCrop(Rect::EMPTY_RECT); data.read(sourceCrop); float frameScale = data.readFloat(); bool childrenOnly = data.readBool(); - status_t res = captureLayers(layerHandleBinder, &outBuffer, sourceCrop, frameScale, - childrenOnly); + status_t res = captureLayers(layerHandleBinder, &outBuffer, reqDataspace, + reqPixelFormat, sourceCrop, frameScale, childrenOnly); reply->writeInt32(res); if (res == NO_ERROR) { reply->write(*outBuffer); @@ -752,26 +881,6 @@ status_t BnSurfaceComposer::onTransact( } return NO_ERROR; } - case GET_DISPLAY_VIEWPORT: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - Rect outViewport; - sp<IBinder> display = nullptr; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to readStrongBinder: %d", result); - return result; - } - result = getDisplayViewport(display, &outViewport); - result = reply->writeInt32(result); - if (result == NO_ERROR) { - result = reply->write(outViewport); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to write: %d", result); - return result; - } - } - return NO_ERROR; - } case GET_ACTIVE_CONFIG: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); @@ -906,6 +1015,115 @@ status_t BnSurfaceComposer::onTransact( } return result; } + case GET_COMPOSITION_PREFERENCE: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + ui::Dataspace defaultDataspace; + ui::PixelFormat defaultPixelFormat; + ui::Dataspace wideColorGamutDataspace; + ui::PixelFormat wideColorGamutPixelFormat; + status_t error = + getCompositionPreference(&defaultDataspace, &defaultPixelFormat, + &wideColorGamutDataspace, &wideColorGamutPixelFormat); + reply->writeInt32(error); + if (error == NO_ERROR) { + reply->writeInt32(static_cast<int32_t>(defaultDataspace)); + reply->writeInt32(static_cast<int32_t>(defaultPixelFormat)); + reply->writeInt32(static_cast<int32_t>(wideColorGamutDataspace)); + reply->writeInt32(static_cast<int32_t>(wideColorGamutPixelFormat)); + } + return NO_ERROR; + } + case GET_COLOR_MANAGEMENT: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + bool result; + status_t error = getColorManagement(&result); + if (error == NO_ERROR) { + reply->writeBool(result); + } + return result; + } + case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + + sp<IBinder> display = data.readStrongBinder(); + ui::PixelFormat format; + ui::Dataspace dataspace; + uint8_t component = 0; + auto result = + getDisplayedContentSamplingAttributes(display, &format, &dataspace, &component); + if (result == NO_ERROR) { + reply->writeUint32(static_cast<uint32_t>(format)); + reply->writeUint32(static_cast<uint32_t>(dataspace)); + reply->writeUint32(static_cast<uint32_t>(component)); + } + return result; + } + case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + + sp<IBinder> display = nullptr; + bool enable = false; + int8_t componentMask = 0; + uint64_t maxFrames = 0; + status_t result = data.readStrongBinder(&display); + if (result != NO_ERROR) { + ALOGE("setDisplayContentSamplingEnabled failure in reading Display token: %d", + result); + return result; + } + + result = data.readBool(&enable); + if (result != NO_ERROR) { + ALOGE("setDisplayContentSamplingEnabled failure in reading enable: %d", result); + return result; + } + + result = data.readByte(static_cast<int8_t*>(&componentMask)); + if (result != NO_ERROR) { + ALOGE("setDisplayContentSamplingEnabled failure in reading component mask: %d", + result); + return result; + } + + result = data.readUint64(&maxFrames); + if (result != NO_ERROR) { + ALOGE("setDisplayContentSamplingEnabled failure in reading max frames: %d", result); + return result; + } + + return setDisplayContentSamplingEnabled(display, enable, + static_cast<uint8_t>(componentMask), maxFrames); + } + case GET_DISPLAYED_CONTENT_SAMPLE: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + + sp<IBinder> display = data.readStrongBinder(); + uint64_t maxFrames = 0; + uint64_t timestamp = 0; + + status_t result = data.readUint64(&maxFrames); + if (result != NO_ERROR) { + ALOGE("getDisplayedContentSample failure in reading max frames: %d", result); + return result; + } + + result = data.readUint64(×tamp); + if (result != NO_ERROR) { + ALOGE("getDisplayedContentSample failure in reading timestamp: %d", result); + return result; + } + + DisplayedFrameStats stats; + result = getDisplayedContentSample(display, maxFrames, timestamp, &stats); + if (result == NO_ERROR) { + reply->writeUint64(stats.numFrames); + reply->writeUint64Vector(stats.component_0_sample); + reply->writeUint64Vector(stats.component_1_sample); + reply->writeUint64Vector(stats.component_2_sample); + reply->writeUint64Vector(stats.component_3_sample); + } + return result; + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp new file mode 100644 index 0000000000..1be55e6f8a --- /dev/null +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -0,0 +1,183 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ITransactionCompletedListener" +//#define LOG_NDEBUG 0 + +#include <gui/ITransactionCompletedListener.h> + +namespace android { + +namespace { // Anonymous + +enum class Tag : uint32_t { + ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION, + LAST = ON_TRANSACTION_COMPLETED, +}; + +} // Anonymous namespace + +status_t SurfaceStats::writeToParcel(Parcel* output) const { + status_t err = output->writeStrongBinder(surfaceControl); + if (err != NO_ERROR) { + return err; + } + err = output->writeInt64(acquireTime); + if (err != NO_ERROR) { + return err; + } + return output->writeBool(releasePreviousBuffer); +} + +status_t SurfaceStats::readFromParcel(const Parcel* input) { + status_t err = input->readStrongBinder(&surfaceControl); + if (err != NO_ERROR) { + return err; + } + err = input->readInt64(&acquireTime); + if (err != NO_ERROR) { + return err; + } + return input->readBool(&releasePreviousBuffer); +} + +status_t TransactionStats::writeToParcel(Parcel* output) const { + status_t err = output->writeInt64(latchTime); + if (err != NO_ERROR) { + return err; + } + if (presentFence) { + err = output->writeBool(true); + if (err != NO_ERROR) { + return err; + } + err = output->write(*presentFence); + } else { + err = output->writeBool(false); + } + if (err != NO_ERROR) { + return err; + } + return output->writeParcelableVector(surfaceStats); +} + +status_t TransactionStats::readFromParcel(const Parcel* input) { + status_t err = input->readInt64(&latchTime); + if (err != NO_ERROR) { + return err; + } + bool hasFence = false; + err = input->readBool(&hasFence); + if (err != NO_ERROR) { + return err; + } + if (hasFence) { + presentFence = new Fence(); + err = input->read(*presentFence); + if (err != NO_ERROR) { + return err; + } + } + return input->readParcelableVector(&surfaceStats); +} + +status_t ListenerStats::writeToParcel(Parcel* output) const { + status_t err = output->writeInt32(static_cast<int32_t>(transactionStats.size())); + if (err != NO_ERROR) { + return err; + } + + for (const auto& [callbackIds, stats] : transactionStats) { + err = output->writeParcelable(stats); + if (err != NO_ERROR) { + return err; + } + err = output->writeInt64Vector(callbackIds); + if (err != NO_ERROR) { + return err; + } + } + return NO_ERROR; +} + +status_t ListenerStats::readFromParcel(const Parcel* input) { + int32_t transactionStats_size = input->readInt32(); + + for (int i = 0; i < transactionStats_size; i++) { + TransactionStats stats; + std::vector<CallbackId> callbackIds; + + status_t err = input->readParcelable(&stats); + if (err != NO_ERROR) { + return err; + } + err = input->readInt64Vector(&callbackIds); + if (err != NO_ERROR) { + return err; + } + + transactionStats.emplace(callbackIds, stats); + } + return NO_ERROR; +} + +ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener, + const std::unordered_set<CallbackId>& callbackIds) { + ListenerStats listenerStats; + listenerStats.listener = listener; + TransactionStats transactionStats; + listenerStats.transactionStats.emplace(std::piecewise_construct, + std::forward_as_tuple(callbackIds.begin(), + callbackIds.end()), + std::forward_as_tuple(transactionStats)); + return listenerStats; +} + +class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> { +public: + explicit BpTransactionCompletedListener(const sp<IBinder>& impl) + : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") { + } + + ~BpTransactionCompletedListener() override; + + void onTransactionCompleted(ListenerStats stats) override { + callRemoteAsync<decltype(&ITransactionCompletedListener:: + onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED, + stats); + } +}; + +// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see +// clang warning -Wweak-vtables) +BpTransactionCompletedListener::~BpTransactionCompletedListener() = default; + +IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener"); + +status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) { + if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { + return BBinder::onTransact(code, data, reply, flags); + } + auto tag = static_cast<Tag>(code); + switch (tag) { + case Tag::ON_TRANSACTION_COMPLETED: + return callLocalAsync(data, reply, + &ITransactionCompletedListener::onTransactionCompleted); + } +} + +}; // namespace android diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp index d3dc16d30e..cdde9a2308 100644 --- a/libs/gui/LayerDebugInfo.cpp +++ b/libs/gui/LayerDebugInfo.cpp @@ -16,13 +16,14 @@ #include <gui/LayerDebugInfo.h> +#include <android-base/stringprintf.h> + #include <ui/DebugUtils.h> #include <binder/Parcel.h> -#include <utils/String8.h> - using namespace android; +using android::base::StringAppendF; #define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false) @@ -42,7 +43,6 @@ status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const { RETURN_ON_ERROR(parcel->writeInt32(mWidth)); RETURN_ON_ERROR(parcel->writeInt32(mHeight)); RETURN_ON_ERROR(parcel->write(mCrop)); - RETURN_ON_ERROR(parcel->write(mFinalCrop)); RETURN_ON_ERROR(parcel->writeFloat(mColor.r)); RETURN_ON_ERROR(parcel->writeFloat(mColor.g)); RETURN_ON_ERROR(parcel->writeFloat(mColor.b)); @@ -81,7 +81,6 @@ status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { RETURN_ON_ERROR(parcel->readInt32(&mWidth)); RETURN_ON_ERROR(parcel->readInt32(&mHeight)); RETURN_ON_ERROR(parcel->read(mCrop)); - RETURN_ON_ERROR(parcel->read(mFinalCrop)); mColor.r = parcel->readFloat(); RETURN_ON_ERROR(parcel->errorCheck()); mColor.g = parcel->readFloat(); @@ -110,39 +109,37 @@ status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { } std::string to_string(const LayerDebugInfo& info) { - String8 result; + std::string result; - result.appendFormat("+ %s (%s)\n", info.mType.c_str(), info.mName.c_str()); + StringAppendF(&result, "+ %s (%s)\n", info.mType.c_str(), info.mName.c_str()); info.mTransparentRegion.dump(result, "TransparentRegion"); info.mVisibleRegion.dump(result, "VisibleRegion"); info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion"); - result.appendFormat(" layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", - info.mLayerStack, info.mZ, static_cast<double>(info.mX), static_cast<double>(info.mY), - info.mWidth, info.mHeight); + StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", + info.mLayerStack, info.mZ, static_cast<double>(info.mX), + static_cast<double>(info.mY), info.mWidth, info.mHeight); - result.appendFormat("crop=%s, finalCrop=%s, ", - to_string(info.mCrop).c_str(), to_string(info.mFinalCrop).c_str()); - result.appendFormat("isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty); - result.appendFormat("dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str()); - result.appendFormat("pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str()); - result.appendFormat("color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ", - static_cast<double>(info.mColor.r), static_cast<double>(info.mColor.g), - static_cast<double>(info.mColor.b), static_cast<double>(info.mColor.a), - info.mFlags); - result.appendFormat("tr=[%.2f, %.2f][%.2f, %.2f]", - static_cast<double>(info.mMatrix[0][0]), static_cast<double>(info.mMatrix[0][1]), - static_cast<double>(info.mMatrix[1][0]), static_cast<double>(info.mMatrix[1][1])); + StringAppendF(&result, "crop=%s, ", to_string(info.mCrop).c_str()); + StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty); + StringAppendF(&result, "dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str()); + StringAppendF(&result, "pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str()); + StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ", + static_cast<double>(info.mColor.r), static_cast<double>(info.mColor.g), + static_cast<double>(info.mColor.b), static_cast<double>(info.mColor.a), + info.mFlags); + StringAppendF(&result, "tr=[%.2f, %.2f][%.2f, %.2f]", static_cast<double>(info.mMatrix[0][0]), + static_cast<double>(info.mMatrix[0][1]), static_cast<double>(info.mMatrix[1][0]), + static_cast<double>(info.mMatrix[1][1])); result.append("\n"); - result.appendFormat(" parent=%s\n", info.mParentName.c_str()); - result.appendFormat(" activeBuffer=[%4ux%4u:%4u,%s],", - info.mActiveBufferWidth, info.mActiveBufferHeight, - info.mActiveBufferStride, - decodePixelFormat(info.mActiveBufferFormat).c_str()); - result.appendFormat(" queued-frames=%d, mRefreshPending=%d", - info.mNumQueuedFrames, info.mRefreshPending); + StringAppendF(&result, " parent=%s\n", info.mParentName.c_str()); + StringAppendF(&result, " activeBuffer=[%4ux%4u:%4u,%s],", info.mActiveBufferWidth, + info.mActiveBufferHeight, info.mActiveBufferStride, + decodePixelFormat(info.mActiveBufferFormat).c_str()); + StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d", info.mNumQueuedFrames, + info.mRefreshPending); result.append("\n"); - return std::string(result.c_str()); + return result; } } // android diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 01acc2de20..35ce6e393a 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +#define LOG_TAG "LayerState" + +#include <inttypes.h> + #include <utils/Errors.h> #include <binder/Parcel.h> #include <gui/ISurfaceComposerClient.h> @@ -25,7 +29,7 @@ namespace android { status_t layer_state_t::write(Parcel& output) const { output.writeStrongBinder(surface); - output.writeUint32(what); + output.writeUint64(what); output.writeFloat(x); output.writeFloat(y); output.writeInt32(z); @@ -37,26 +41,66 @@ status_t layer_state_t::write(Parcel& output) const output.writeUint32(mask); *reinterpret_cast<layer_state_t::matrix22_t *>( output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix; - output.write(crop); - output.write(finalCrop); - output.writeStrongBinder(barrierHandle); + output.write(crop_legacy); + output.writeStrongBinder(barrierHandle_legacy); output.writeStrongBinder(reparentHandle); - output.writeUint64(frameNumber); + output.writeUint64(frameNumber_legacy); output.writeInt32(overrideScalingMode); - output.writeStrongBinder(IInterface::asBinder(barrierGbp)); + output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy)); output.writeStrongBinder(relativeLayerHandle); output.writeStrongBinder(parentHandleForChild); output.writeFloat(color.r); output.writeFloat(color.g); output.writeFloat(color.b); +#ifndef NO_INPUT + inputInfo.write(output); +#endif output.write(transparentRegion); + output.writeUint32(transform); + output.writeBool(transformToDisplayInverse); + output.write(crop); + output.write(frame); + if (buffer) { + output.writeBool(true); + output.write(*buffer); + } else { + output.writeBool(false); + } + if (acquireFence) { + output.writeBool(true); + output.write(*acquireFence); + } else { + output.writeBool(false); + } + output.writeUint32(static_cast<uint32_t>(dataspace)); + output.write(hdrMetadata); + output.write(surfaceDamageRegion); + output.writeInt32(api); + if (sidebandStream) { + output.writeBool(true); + output.writeNativeHandle(sidebandStream->handle()); + } else { + output.writeBool(false); + } + + memcpy(output.writeInplace(16 * sizeof(float)), + colorTransform.asArray(), 16 * sizeof(float)); + output.writeFloat(cornerRadius); + + if (output.writeVectorSize(listenerCallbacks) == NO_ERROR) { + for (const auto& [listener, callbackIds] : listenerCallbacks) { + output.writeStrongBinder(IInterface::asBinder(listener)); + output.writeInt64Vector(callbackIds); + } + } + return NO_ERROR; } status_t layer_state_t::read(const Parcel& input) { surface = input.readStrongBinder(); - what = input.readUint32(); + what = input.readUint64(); x = input.readFloat(); y = input.readFloat(); z = input.readInt32(); @@ -72,20 +116,54 @@ status_t layer_state_t::read(const Parcel& input) } else { return BAD_VALUE; } - input.read(crop); - input.read(finalCrop); - barrierHandle = input.readStrongBinder(); + input.read(crop_legacy); + barrierHandle_legacy = input.readStrongBinder(); reparentHandle = input.readStrongBinder(); - frameNumber = input.readUint64(); + frameNumber_legacy = input.readUint64(); overrideScalingMode = input.readInt32(); - barrierGbp = - interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); + barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); relativeLayerHandle = input.readStrongBinder(); parentHandleForChild = input.readStrongBinder(); color.r = input.readFloat(); color.g = input.readFloat(); color.b = input.readFloat(); + +#ifndef NO_INPUT + inputInfo = InputWindowInfo::read(input); +#endif + input.read(transparentRegion); + transform = input.readUint32(); + transformToDisplayInverse = input.readBool(); + input.read(crop); + input.read(frame); + buffer = new GraphicBuffer(); + if (input.readBool()) { + input.read(*buffer); + } + acquireFence = new Fence(); + if (input.readBool()) { + input.read(*acquireFence); + } + dataspace = static_cast<ui::Dataspace>(input.readUint32()); + input.read(hdrMetadata); + input.read(surfaceDamageRegion); + api = input.readInt32(); + if (input.readBool()) { + sidebandStream = NativeHandle::create(input.readNativeHandle(), true); + } + + colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float)))); + cornerRadius = input.readFloat(); + + int32_t listenersSize = input.readInt32(); + for (int32_t i = 0; i < listenersSize; i++) { + auto listener = interface_cast<ITransactionCompletedListener>(input.readStrongBinder()); + std::vector<CallbackId> callbackIds; + input.readInt64Vector(&callbackIds); + listenerCallbacks.emplace_back(listener, callbackIds); + } + return NO_ERROR; } @@ -194,19 +272,19 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eLayerStackChanged; layerStack = other.layerStack; } - if (other.what & eCropChanged) { - what |= eCropChanged; - crop = other.crop; + if (other.what & eCropChanged_legacy) { + what |= eCropChanged_legacy; + crop_legacy = other.crop_legacy; } - if (other.what & eDeferTransaction) { - what |= eDeferTransaction; - barrierHandle = other.barrierHandle; - barrierGbp = other.barrierGbp; - frameNumber = other.frameNumber; + if (other.what & eCornerRadiusChanged) { + what |= eCornerRadiusChanged; + cornerRadius = other.cornerRadius; } - if (other.what & eFinalCropChanged) { - what |= eFinalCropChanged; - finalCrop = other.finalCrop; + if (other.what & eDeferTransaction_legacy) { + what |= eDeferTransaction_legacy; + barrierHandle_legacy = other.barrierHandle_legacy; + barrierGbp_legacy = other.barrierGbp_legacy; + frameNumber_legacy = other.frameNumber_legacy; } if (other.what & eOverrideScalingModeChanged) { what |= eOverrideScalingModeChanged; @@ -234,6 +312,71 @@ void layer_state_t::merge(const layer_state_t& other) { if (other.what & eDestroySurface) { what |= eDestroySurface; } + if (other.what & eTransformChanged) { + what |= eTransformChanged; + transform = other.transform; + } + if (other.what & eTransformToDisplayInverseChanged) { + what |= eTransformToDisplayInverseChanged; + transformToDisplayInverse = other.transformToDisplayInverse; + } + if (other.what & eCropChanged) { + what |= eCropChanged; + crop = other.crop; + } + if (other.what & eFrameChanged) { + what |= eFrameChanged; + frame = other.frame; + } + if (other.what & eBufferChanged) { + what |= eBufferChanged; + buffer = other.buffer; + } + if (other.what & eAcquireFenceChanged) { + what |= eAcquireFenceChanged; + acquireFence = other.acquireFence; + } + if (other.what & eDataspaceChanged) { + what |= eDataspaceChanged; + dataspace = other.dataspace; + } + if (other.what & eHdrMetadataChanged) { + what |= eHdrMetadataChanged; + hdrMetadata = other.hdrMetadata; + } + if (other.what & eSurfaceDamageRegionChanged) { + what |= eSurfaceDamageRegionChanged; + surfaceDamageRegion = other.surfaceDamageRegion; + } + if (other.what & eApiChanged) { + what |= eApiChanged; + api = other.api; + } + if (other.what & eSidebandStreamChanged) { + what |= eSidebandStreamChanged; + sidebandStream = other.sidebandStream; + } + if (other.what & eColorTransformChanged) { + what |= eColorTransformChanged; + colorTransform = other.colorTransform; + } + if (other.what & eListenerCallbacksChanged) { + what |= eListenerCallbacksChanged; + listenerCallbacks = other.listenerCallbacks; + } + +#ifndef NO_INPUT + if (other.what & eInputInfoChanged) { + what |= eInputInfoChanged; + inputInfo = other.inputInfo; + } +#endif + + if ((other.what & what) != other.what) { + ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " + "other.what=0x%" PRIu64 " what=0x%" PRIu64, + other.what, what); + } } }; // namespace android diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp index 52c906775e..2f8e104ea0 100644 --- a/libs/gui/StreamSplitter.cpp +++ b/libs/gui/StreamSplitter.cpp @@ -38,11 +38,11 @@ namespace android { status_t StreamSplitter::createSplitter( const sp<IGraphicBufferConsumer>& inputQueue, sp<StreamSplitter>* outSplitter) { - if (inputQueue == NULL) { + if (inputQueue == nullptr) { ALOGE("createSplitter: inputQueue must not be NULL"); return BAD_VALUE; } - if (outSplitter == NULL) { + if (outSplitter == nullptr) { ALOGE("createSplitter: outSplitter must not be NULL"); return BAD_VALUE; } @@ -74,7 +74,7 @@ StreamSplitter::~StreamSplitter() { status_t StreamSplitter::addOutput( const sp<IGraphicBufferProducer>& outputQueue) { - if (outputQueue == NULL) { + if (outputQueue == nullptr) { ALOGE("addOutput: outputQueue must not be NULL"); return BAD_VALUE; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 2de14c8846..00e23f0df1 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -156,7 +156,7 @@ status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) { ATRACE_CALL(); DisplayStatInfo stats; - status_t result = composerService()->getDisplayStats(NULL, &stats); + status_t result = composerService()->getDisplayStats(nullptr, &stats); if (result != NO_ERROR) { return result; } @@ -501,7 +501,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot != BufferItem::INVALID_BUFFER_SLOT) { sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer); - if (gbuf != NULL) { + if (gbuf != nullptr) { *buffer = gbuf.get(); *fenceFd = -1; return OK; @@ -541,7 +541,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { sp<GraphicBuffer>& gbuf(mSlots[buf].buffer); // this should never happen - ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf); + ALOGE_IF(fence == nullptr, "Surface::dequeueBuffer: received null Fence! buf=%d", buf); if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { freeAllBuffers(); @@ -619,7 +619,7 @@ int Surface::cancelBuffer(android_native_buffer_t* buffer, int Surface::getSlotFromBufferLocked( android_native_buffer_t* buffer) const { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (mSlots[i].buffer != NULL && + if (mSlots[i].buffer != nullptr && mSlots[i].buffer->handle == buffer->handle) { return i; } @@ -965,6 +965,9 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA: res = dispatchSetBuffersCta8613Metadata(args); break; + case NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA: + res = dispatchSetBuffersHdr10PlusMetadata(args); + break; case NATIVE_WINDOW_SET_SURFACE_DAMAGE: res = dispatchSetSurfaceDamage(args); break; @@ -1120,6 +1123,12 @@ int Surface::dispatchSetBuffersCta8613Metadata(va_list args) { return setBuffersCta8613Metadata(metadata); } +int Surface::dispatchSetBuffersHdr10PlusMetadata(va_list args) { + const size_t size = va_arg(args, size_t); + const uint8_t* metadata = va_arg(args, const uint8_t*); + return setBuffersHdr10PlusMetadata(size, metadata); +} + int Surface::dispatchSetSurfaceDamage(va_list args) { android_native_rect_t* rects = va_arg(args, android_native_rect_t*); size_t numRects = va_arg(args, size_t); @@ -1268,7 +1277,7 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, ATRACE_CALL(); ALOGV("Surface::detachNextBuffer"); - if (outBuffer == NULL || outFence == NULL) { + if (outBuffer == nullptr || outFence == nullptr) { return BAD_VALUE; } @@ -1277,8 +1286,8 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, mRemovedBuffers.clear(); } - sp<GraphicBuffer> buffer(NULL); - sp<Fence> fence(NULL); + sp<GraphicBuffer> buffer(nullptr); + sp<Fence> fence(nullptr); status_t result = mGraphicBufferProducer->detachNextBuffer( &buffer, &fence); if (result != NO_ERROR) { @@ -1286,19 +1295,19 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, } *outBuffer = buffer; - if (fence != NULL && fence->isValid()) { + if (fence != nullptr && fence->isValid()) { *outFence = fence; } else { *outFence = Fence::NO_FENCE; } for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (mSlots[i].buffer != NULL && + if (mSlots[i].buffer != nullptr && mSlots[i].buffer->getId() == buffer->getId()) { if (mReportRemovedBuffers) { mRemovedBuffers.push_back(mSlots[i].buffer); } - mSlots[i].buffer = NULL; + mSlots[i].buffer = nullptr; } } @@ -1349,7 +1358,7 @@ int Surface::setCrop(Rect const* rect) ATRACE_CALL(); Rect realRect(Rect::EMPTY_RECT); - if (rect == NULL || rect->isEmpty()) { + if (rect == nullptr || rect->isEmpty()) { realRect.clear(); } else { realRect = *rect; @@ -1568,6 +1577,19 @@ int Surface::setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata return NO_ERROR; } +int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) { + ALOGV("Surface::setBuffersBlobMetadata"); + Mutex::Autolock lock(mMutex); + if (size > 0) { + mHdrMetadata.hdr10plus.assign(metadata, metadata + size); + mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS; + } else { + mHdrMetadata.validTypes &= ~HdrMetadata::HDR10PLUS; + mHdrMetadata.hdr10plus.clear(); + } + return NO_ERROR; +} + Dataspace Surface::getBuffersDataSpace() { ALOGV("Surface::getBuffersDataSpace"); Mutex::Autolock lock(mMutex); @@ -1576,7 +1598,7 @@ Dataspace Surface::getBuffersDataSpace() { void Surface::freeAllBuffers() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - mSlots[i].buffer = 0; + mSlots[i].buffer = nullptr; } } @@ -1616,12 +1638,12 @@ static status_t copyBlt( // src and dst with, height and format must be identical. no verification // is done here. status_t err; - uint8_t* src_bits = NULL; + uint8_t* src_bits = nullptr; err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), reinterpret_cast<void**>(&src_bits)); ALOGE_IF(err, "error locking src buffer %s", strerror(-err)); - uint8_t* dst_bits = NULL; + uint8_t* dst_bits = nullptr; err = dst->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), reinterpret_cast<void**>(&dst_bits), *dstFenceFd); ALOGE_IF(err, "error locking dst buffer %s", strerror(-err)); @@ -1669,7 +1691,7 @@ static status_t copyBlt( status_t Surface::lock( ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { - if (mLockedBuffer != 0) { + if (mLockedBuffer != nullptr) { ALOGE("Surface::lock failed, already locked"); return INVALID_OPERATION; } @@ -1701,7 +1723,7 @@ status_t Surface::lock( // figure out if we can copy the frontbuffer back const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); - const bool canCopyBack = (frontBuffer != 0 && + const bool canCopyBack = (frontBuffer != nullptr && backBuffer->width == frontBuffer->width && backBuffer->height == frontBuffer->height && backBuffer->format == frontBuffer->format); @@ -1763,7 +1785,7 @@ status_t Surface::lock( status_t Surface::unlockAndPost() { - if (mLockedBuffer == 0) { + if (mLockedBuffer == nullptr) { ALOGE("Surface::unlockAndPost failed, no locked buffer"); return INVALID_OPERATION; } @@ -1777,7 +1799,7 @@ status_t Surface::unlockAndPost() mLockedBuffer->handle, strerror(-err)); mPostedBuffer = mLockedBuffer; - mLockedBuffer = 0; + mLockedBuffer = nullptr; return err; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index f3c6fd2f87..95862199eb 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -25,7 +25,9 @@ #include <utils/String8.h> #include <utils/threads.h> +#include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <binder/ProcessState.h> #include <system/graphics.h> @@ -40,6 +42,10 @@ #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> +#ifndef NO_INPUT +#include <input/InputWindow.h> +#endif + #include <private/gui/ComposerService.h> namespace android { @@ -60,7 +66,7 @@ void ComposerService::connectLocked() { while (getService(name, &mComposerService) != NO_ERROR) { usleep(250000); } - assert(mComposerService != NULL); + assert(mComposerService != nullptr); // Create the death listener. class DeathObserver : public IBinder::DeathRecipient { @@ -81,9 +87,9 @@ void ComposerService::connectLocked() { /*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() { ComposerService& instance = ComposerService::getInstance(); Mutex::Autolock _l(instance.mLock); - if (instance.mComposerService == NULL) { + if (instance.mComposerService == nullptr) { ComposerService::getInstance().connectLocked(); - assert(instance.mComposerService != NULL); + assert(instance.mComposerService != nullptr); ALOGD("ComposerService reconnected"); } return instance.mComposerService; @@ -92,8 +98,63 @@ void ComposerService::connectLocked() { void ComposerService::composerServiceDied() { Mutex::Autolock _l(mLock); - mComposerService = NULL; - mDeathObserver = NULL; + mComposerService = nullptr; + mDeathObserver = nullptr; +} + +// --------------------------------------------------------------------------- + +// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs +// to be able to return a sp<> to its instance to pass to SurfaceFlinger. +// ANDROID_SINGLETON_STATIC_INSTANCE only allows a reference to an instance. + +// 0 is an invalid callback id +TransactionCompletedListener::TransactionCompletedListener() : mCallbackIdCounter(1) {} + +CallbackId TransactionCompletedListener::getNextIdLocked() { + return mCallbackIdCounter++; +} + +sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() { + static sp<TransactionCompletedListener> sInstance = new TransactionCompletedListener; + return sInstance; +} + +sp<ITransactionCompletedListener> TransactionCompletedListener::getIInstance() { + return static_cast<sp<ITransactionCompletedListener>>(getInstance()); +} + +void TransactionCompletedListener::startListeningLocked() { + if (mListening) { + return; + } + ProcessState::self()->startThreadPool(); + mListening = true; +} + +CallbackId TransactionCompletedListener::addCallback(const TransactionCompletedCallback& callback) { + std::lock_guard<std::mutex> lock(mMutex); + startListeningLocked(); + + CallbackId callbackId = getNextIdLocked(); + mCallbacks.emplace(callbackId, callback); + return callbackId; +} + +void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { + std::lock_guard lock(mMutex); + + for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) { + for (auto callbackId : callbackIds) { + const auto& callback = mCallbacks[callbackId]; + if (!callback) { + ALOGE("cannot call null callback function, skipping"); + continue; + } + callback(transactionStats); + mCallbacks.erase(callbackId); + } + } } // --------------------------------------------------------------------------- @@ -127,6 +188,17 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr } other.mDisplayStates.clear(); + for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) { + auto& [callbackIds, surfaceControls] = callbackInfo; + mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator( + callbackIds.begin()), + std::make_move_iterator(callbackIds.end())); + mListenerCallbacks[listener] + .surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()), + std::make_move_iterator(surfaceControls.end())); + } + other.mListenerCallbacks.clear(); + return *this; } @@ -137,6 +209,32 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + // For every listener with registered callbacks + for (const auto& [listener, callbackInfo] : mListenerCallbacks) { + auto& [callbackIds, surfaceControls] = callbackInfo; + if (callbackIds.empty()) { + continue; + } + + // If the listener does not have any SurfaceControls set on this Transaction, send the + // callback now + if (surfaceControls.empty()) { + listener->onTransactionCompleted(ListenerStats::createEmpty(listener, callbackIds)); + } + + // If the listener has any SurfaceControls set on this Transaction update the surface state + for (const auto& surfaceControl : surfaceControls) { + layer_state_t* s = getLayerState(surfaceControl); + if (!s) { + ALOGE("failed to get layer state"); + continue; + } + s->what |= layer_state_t::eListenerCallbacksChanged; + s->listenerCallbacks.emplace_back(listener, std::move(callbackIds)); + } + } + mListenerCallbacks.clear(); + Vector<ComposerState> composerStates; Vector<DisplayState> displayStates; uint32_t flags = 0; @@ -206,6 +304,11 @@ layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<Surfac return &(mComposerStates[sc].state); } +void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( + const sp<SurfaceControl>& sc) { + mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls.insert(sc); +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition( const sp<SurfaceControl>& sc, float x, float y) { layer_state_t* s = getLayerState(sc); @@ -216,6 +319,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosit s->what |= layer_state_t::ePositionChanged; s->x = x; s->y = y; + + registerSurfaceControlForCallback(sc); return *this; } @@ -240,9 +345,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSize( s->w = w; s->h = h; - // Resizing a surface makes the transaction synchronous. - mForceSynchronous = true; - + registerSurfaceControlForCallback(sc); return *this; } @@ -255,6 +358,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayer } s->what |= layer_state_t::eLayerChanged; s->z = z; + + registerSurfaceControlForCallback(sc); return *this; } @@ -267,6 +372,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelat s->what |= layer_state_t::eRelativeLayerChanged; s->relativeLayerHandle = relativeTo; s->z = z; + + registerSurfaceControlForCallback(sc); return *this; } @@ -286,6 +393,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFlags s->flags &= ~mask; s->flags |= (flags & mask); s->mask |= mask; + + registerSurfaceControlForCallback(sc); return *this; } @@ -299,6 +408,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrans } s->what |= layer_state_t::eTransparentRegionChanged; s->transparentRegion = transparentRegion; + + registerSurfaceControlForCallback(sc); return *this; } @@ -311,6 +422,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAlpha } s->what |= layer_state_t::eAlphaChanged; s->alpha = alpha; + + registerSurfaceControlForCallback(sc); return *this; } @@ -323,6 +436,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayer } s->what |= layer_state_t::eLayerStackChanged; s->layerStack = layerStack; + + registerSurfaceControlForCallback(sc); return *this; } @@ -341,57 +456,68 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMatri matrix.dsdy = dsdy; matrix.dtdy = dtdy; s->matrix = matrix; + + registerSurfaceControlForCallback(sc); return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop( +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop_legacy( const sp<SurfaceControl>& sc, const Rect& crop) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eCropChanged; - s->crop = crop; + s->what |= layer_state_t::eCropChanged_legacy; + s->crop_legacy = crop; + + registerSurfaceControlForCallback(sc); return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFinalCrop(const sp<SurfaceControl>& sc, const Rect& crop) { +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCornerRadius( + const sp<SurfaceControl>& sc, float cornerRadius) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eFinalCropChanged; - s->finalCrop = crop; + s->what |= layer_state_t::eCornerRadiusChanged; + s->cornerRadius = cornerRadius; return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil( - const sp<SurfaceControl>& sc, - const sp<IBinder>& handle, uint64_t frameNumber) { +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<IBinder>& handle, + uint64_t frameNumber) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eDeferTransaction; - s->barrierHandle = handle; - s->frameNumber = frameNumber; + s->what |= layer_state_t::eDeferTransaction_legacy; + s->barrierHandle_legacy = handle; + s->frameNumber_legacy = frameNumber; + + registerSurfaceControlForCallback(sc); return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil( - const sp<SurfaceControl>& sc, - const sp<Surface>& barrierSurface, uint64_t frameNumber) { +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<Surface>& barrierSurface, + uint64_t frameNumber) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eDeferTransaction; - s->barrierGbp = barrierSurface->getIGraphicBufferProducer(); - s->frameNumber = frameNumber; + s->what |= layer_state_t::eDeferTransaction_legacy; + s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer(); + s->frameNumber_legacy = frameNumber; + + registerSurfaceControlForCallback(sc); return *this; } @@ -405,6 +531,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent } s->what |= layer_state_t::eReparentChildren; s->reparentHandle = newParentHandle; + + registerSurfaceControlForCallback(sc); return *this; } @@ -418,6 +546,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent } s->what |= layer_state_t::eReparent; s->parentHandleForChild = newParentHandle; + + registerSurfaceControlForCallback(sc); return *this; } @@ -431,6 +561,177 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColor } s->what |= layer_state_t::eColorChanged; s->color = color; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTransform( + const sp<SurfaceControl>& sc, uint32_t transform) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eTransformChanged; + s->transform = transform; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::setTransformToDisplayInverse(const sp<SurfaceControl>& sc, + bool transformToDisplayInverse) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eTransformToDisplayInverseChanged; + s->transformToDisplayInverse = transformToDisplayInverse; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop( + const sp<SurfaceControl>& sc, const Rect& crop) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eCropChanged; + s->crop = crop; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame( + const sp<SurfaceControl>& sc, const Rect& frame) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eFrameChanged; + s->frame = frame; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer( + const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eBufferChanged; + s->buffer = buffer; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence( + const sp<SurfaceControl>& sc, const sp<Fence>& fence) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eAcquireFenceChanged; + s->acquireFence = fence; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace( + const sp<SurfaceControl>& sc, ui::Dataspace dataspace) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eDataspaceChanged; + s->dataspace = dataspace; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setHdrMetadata( + const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eHdrMetadataChanged; + s->hdrMetadata = hdrMetadata; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSurfaceDamageRegion( + const sp<SurfaceControl>& sc, const Region& surfaceDamageRegion) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eSurfaceDamageRegionChanged; + s->surfaceDamageRegion = surfaceDamageRegion; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApi( + const sp<SurfaceControl>& sc, int32_t api) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eApiChanged; + s->api = api; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSidebandStream( + const sp<SurfaceControl>& sc, const sp<NativeHandle>& sidebandStream) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eSidebandStreamChanged; + s->sidebandStream = sidebandStream; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::addTransactionCompletedCallback( + TransactionCompletedCallbackTakesContext callback, void* callbackContext) { + auto listener = TransactionCompletedListener::getInstance(); + + auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1); + + CallbackId callbackId = listener->addCallback(callbackWithContext); + + mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace( + callbackId); return *this; } @@ -441,6 +742,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachCh mStatus = BAD_INDEX; } s->what |= layer_state_t::eDetachChildren; + + registerSurfaceControlForCallback(sc); return *this; } @@ -468,6 +771,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setOverr s->what |= layer_state_t::eOverrideScalingModeChanged; s->overrideScalingMode = overrideScalingMode; + + registerSurfaceControlForCallback(sc); return *this; } @@ -479,8 +784,25 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeome return *this; } s->what |= layer_state_t::eGeometryAppliesWithResize; + + registerSurfaceControlForCallback(sc); + return *this; +} + +#ifndef NO_INPUT +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo( + const sp<SurfaceControl>& sc, + const InputWindowInfo& info) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->inputInfo = info; + s->what |= layer_state_t::eInputInfoChanged; return *this; } +#endif SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::destroySurface( const sp<SurfaceControl>& sc) { @@ -493,6 +815,20 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::destroyS return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorTransform( + const sp<SurfaceControl>& sc, const mat3& matrix, const vec3& translation) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eColorTransformChanged; + s->colorTransform = mat4(matrix, translation); + + registerSurfaceControlForCallback(sc); + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { @@ -571,12 +907,12 @@ SurfaceComposerClient::SurfaceComposerClient(const sp<ISurfaceComposerClient>& c void SurfaceComposerClient::onFirstRef() { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - if (sf != 0 && mStatus == NO_INIT) { + if (sf != nullptr && mStatus == NO_INIT) { auto rootProducer = mParent.promote(); sp<ISurfaceComposerClient> conn; conn = (rootProducer != nullptr) ? sf->createScopedConnection(rootProducer) : sf->createConnection(); - if (conn != 0) { + if (conn != nullptr) { mClient = conn; mStatus = NO_ERROR; } @@ -606,7 +942,7 @@ void SurfaceComposerClient::dispose() { // this can be called more than once. sp<ISurfaceComposerClient> client; Mutex::Autolock _lm(mLock); - if (mClient != 0) { + if (mClient != nullptr) { client = mClient; // hold ref while lock is held mClient.clear(); } @@ -718,10 +1054,6 @@ status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, return NO_ERROR; } -status_t SurfaceComposerClient::getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) { - return ComposerService::getComposerService()->getDisplayViewport(display, outViewport); -} - int SurfaceComposerClient::getActiveConfig(const sp<IBinder>& display) { return ComposerService::getComposerService()->getActiveConfig(display); } @@ -749,6 +1081,14 @@ void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token, ComposerService::getComposerService()->setPowerMode(token, mode); } +status_t SurfaceComposerClient::getCompositionPreference( + ui::Dataspace* defaultDataspace, ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, ui::PixelFormat* wideColorGamutPixelFormat) { + return ComposerService::getComposerService() + ->getCompositionPreference(defaultDataspace, defaultPixelFormat, + wideColorGamutDataspace, wideColorGamutPixelFormat); +} + status_t SurfaceComposerClient::clearAnimationFrameStats() { return ComposerService::getComposerService()->clearAnimationFrameStats(); } @@ -763,16 +1103,39 @@ status_t SurfaceComposerClient::getHdrCapabilities(const sp<IBinder>& display, outCapabilities); } +status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display, + ui::PixelFormat* outFormat, + ui::Dataspace* outDataspace, + uint8_t* outComponentMask) { + return ComposerService::getComposerService() + ->getDisplayedContentSamplingAttributes(display, outFormat, outDataspace, + outComponentMask); +} + +status_t SurfaceComposerClient::setDisplayContentSamplingEnabled(const sp<IBinder>& display, + bool enable, uint8_t componentMask, + uint64_t maxFrames) { + return ComposerService::getComposerService()->setDisplayContentSamplingEnabled(display, enable, + componentMask, + maxFrames); +} + +status_t SurfaceComposerClient::getDisplayedContentSample(const sp<IBinder>& display, + uint64_t maxFrames, uint64_t timestamp, + DisplayedFrameStats* outStats) { + return ComposerService::getComposerService()->getDisplayedContentSample(display, maxFrames, + timestamp, outStats); +} // ---------------------------------------------------------------------------- -status_t ScreenshotClient::capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, - bool useIdentityTransform, uint32_t rotation, - sp<GraphicBuffer>* outBuffer) { +status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + uint32_t rotation, sp<GraphicBuffer>* outBuffer) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureScreen(display, outBuffer, sourceCrop, reqWidth, reqHeight, minLayerZ, - maxLayerZ, useIdentityTransform, + if (s == nullptr) return NO_INIT; + status_t ret = s->captureScreen(display, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop, + reqWidth, reqHeight, useIdentityTransform, static_cast<ISurfaceComposer::Rotation>(rotation)); if (ret != NO_ERROR) { return ret; @@ -780,21 +1143,25 @@ status_t ScreenshotClient::capture(const sp<IBinder>& display, Rect sourceCrop, return ret; } -status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, +status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, + const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, float frameScale, sp<GraphicBuffer>* outBuffer) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale, - false /* childrenOnly */); + if (s == nullptr) return NO_INIT; + status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, + sourceCrop, frameScale, false /* childrenOnly */); return ret; } -status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, +status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle, + const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, float frameScale, sp<GraphicBuffer>* outBuffer) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale, - true /* childrenOnly */); + if (s == nullptr) return NO_INIT; + status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, + sourceCrop, frameScale, true /* childrenOnly */); return ret; } // ---------------------------------------------------------------------------- diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 5eafbb3555..3a6dfdada5 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -86,7 +86,7 @@ void SurfaceControl::clear() } void SurfaceControl::disconnect() { - if (mGraphicBufferProducer != NULL) { + if (mGraphicBufferProducer != nullptr) { mGraphicBufferProducer->disconnect( BufferQueueCore::CURRENTLY_CONNECTED_API); } @@ -95,28 +95,28 @@ void SurfaceControl::disconnect() { bool SurfaceControl::isSameSurface( const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) { - if (lhs == 0 || rhs == 0) + if (lhs == nullptr || rhs == nullptr) return false; return lhs->mHandle == rhs->mHandle; } status_t SurfaceControl::clearLayerFrameStats() const { status_t err = validate(); - if (err < 0) return err; + if (err != NO_ERROR) return err; const sp<SurfaceComposerClient>& client(mClient); return client->clearLayerFrameStats(mHandle); } status_t SurfaceControl::getLayerFrameStats(FrameStats* outStats) const { status_t err = validate(); - if (err < 0) return err; + if (err != NO_ERROR) return err; const sp<SurfaceComposerClient>& client(mClient); return client->getLayerFrameStats(mHandle, outStats); } status_t SurfaceControl::validate() const { - if (mHandle==0 || mClient==0) { + if (mHandle==nullptr || mClient==nullptr) { ALOGE("invalid handle (%p) or client (%p)", mHandle.get(), mClient.get()); return NO_INIT; @@ -128,7 +128,7 @@ status_t SurfaceControl::writeSurfaceToParcel( const sp<SurfaceControl>& control, Parcel* parcel) { sp<IGraphicBufferProducer> bp; - if (control != NULL) { + if (control != nullptr) { bp = control->mGraphicBufferProducer; } return parcel->writeStrongBinder(IInterface::asBinder(bp)); @@ -146,7 +146,7 @@ sp<Surface> SurfaceControl::generateSurfaceLocked() const sp<Surface> SurfaceControl::getSurface() const { Mutex::Autolock _l(mLock); - if (mSurfaceData == 0) { + if (mSurfaceData == nullptr) { return generateSurfaceLocked(); } return mSurfaceData; diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp index afa15c5cda..fcae05c8ad 100644 --- a/libs/gui/SyncFeatures.cpp +++ b/libs/gui/SyncFeatures.cpp @@ -41,7 +41,7 @@ SyncFeatures::SyncFeatures() : Singleton<SyncFeatures>(), // This can only be called after EGL has been initialized; otherwise the // check below will abort. const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - LOG_ALWAYS_FATAL_IF(exts == NULL, "eglQueryStringImplementationANDROID failed"); + LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed"); if (strstr(exts, "EGL_ANDROID_native_fence_sync")) { // This makes GLConsumer use the EGL_ANDROID_native_fence_sync // extension to create Android native fences to signal when all diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp index b1e44bb8ad..e64ba9bcdd 100644 --- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp @@ -100,7 +100,12 @@ inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) { */ // convert: Return<Status> -> status_t inline status_t toStatusT(Return<Status> const& t) { - return t.isOk() ? static_cast<status_t>(static_cast<Status>(t)) : UNKNOWN_ERROR; + if (t.isOk()) { + return static_cast<status_t>(static_cast<Status>(t)); + } else if (t.isDeadObject()) { + return DEAD_OBJECT; + } + return UNKNOWN_ERROR; } /** @@ -111,7 +116,7 @@ inline status_t toStatusT(Return<Status> const& t) { */ // convert: Return<void> -> status_t inline status_t toStatusT(Return<void> const& t) { - return t.isOk() ? OK : UNKNOWN_ERROR; + return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR); } /** diff --git a/libs/gui/include/gui/BufferHubProducer.h b/libs/gui/include/gui/BufferHubProducer.h index 23c9909826..f7af19b20e 100644 --- a/libs/gui/include/gui/BufferHubProducer.h +++ b/libs/gui/include/gui/BufferHubProducer.h @@ -165,6 +165,10 @@ private: // buffers are acquired by the consumer, we can't . status_t FreeAllBuffers(); + // Helper function that implements the detachBuffer() call, but assuming |mutex_| has been + // locked already. + status_t DetachBufferLocked(size_t slot); + // Concreate implementation backed by BufferHubBuffer. std::shared_ptr<dvr::ProducerQueue> queue_; diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h index d375611e5b..806fbe8aa0 100644 --- a/libs/gui/include/gui/CpuConsumer.h +++ b/libs/gui/include/gui/CpuConsumer.h @@ -70,7 +70,7 @@ class CpuConsumer : public ConsumerBase uint32_t chromaStep; LockedBuffer() : - data(NULL), + data(nullptr), width(0), height(0), format(PIXEL_FORMAT_NONE), @@ -82,8 +82,8 @@ class CpuConsumer : public ConsumerBase dataSpace(HAL_DATASPACE_UNKNOWN), frameNumber(0), flexFormat(PIXEL_FORMAT_NONE), - dataCb(NULL), - dataCr(NULL), + dataCb(nullptr), + dataCr(nullptr), chromaStride(0), chromaStep(0) {} diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h index e06e40f2a6..df02494bf4 100644 --- a/libs/gui/include/gui/FrameTimestamps.h +++ b/libs/gui/include/gui/FrameTimestamps.h @@ -30,7 +30,6 @@ namespace android { struct FrameEvents; class FrameEventHistoryDelta; -class String8; // Identifiers for all the events that may be recorded or reported. @@ -72,7 +71,7 @@ struct FrameEvents { bool hasDequeueReadyInfo() const; void checkFencesForCompletion(); - void dump(String8& outString) const; + void dump(std::string& outString) const; bool valid{false}; int connectId{0}; @@ -112,7 +111,7 @@ public: FrameEvents* getFrame(uint64_t frameNumber); FrameEvents* getFrame(uint64_t frameNumber, size_t* iHint); void checkFencesForCompletion(); - void dump(String8& outString) const; + void dump(std::string& outString) const; static constexpr size_t MAX_FRAME_HISTORY = 8; diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index 71ed3bf239..46a99e124c 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -140,7 +140,8 @@ public: // Scale the crop down horizontally or vertically such that it has the // same aspect ratio as the buffer does. - static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); + static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, + uint32_t bufferHeight); // getTimestamp retrieves the timestamp associated with the texture image // set by the most recent call to updateTexImage. @@ -305,7 +306,6 @@ private: // createIfNeeded creates an EGLImage if required (we haven't created // one yet, or the EGLDisplay or crop-rect has changed). status_t createIfNeeded(EGLDisplay display, - const Rect& cropRect, bool forceCreate = false); // This calls glEGLImageTargetTexture2DOES to bind the image to the @@ -314,7 +314,7 @@ private: const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; } const native_handle* graphicBufferHandle() { - return mGraphicBuffer == NULL ? NULL : mGraphicBuffer->handle; + return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; } private: @@ -324,7 +324,7 @@ private: // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer, const Rect& crop); + const sp<GraphicBuffer>& graphicBuffer); // Disallow copying EglImage(const EglImage& rhs); diff --git a/libs/gui/include/gui/GuiConfig.h b/libs/gui/include/gui/GuiConfig.h index b020ed9b6a..7aa54321fd 100644 --- a/libs/gui/include/gui/GuiConfig.h +++ b/libs/gui/include/gui/GuiConfig.h @@ -17,12 +17,12 @@ #ifndef ANDROID_GUI_CONFIG_H #define ANDROID_GUI_CONFIG_H -#include <utils/String8.h> +#include <string> namespace android { // Append the libgui configuration details to configStr. -void appendGuiConfigString(String8& configStr); +void appendGuiConfigString(std::string& configStr); }; // namespace android diff --git a/libs/gui/include/gui/HdrMetadata.h b/libs/gui/include/gui/HdrMetadata.h index 9800602d6c..1e9c3e7102 100644 --- a/libs/gui/include/gui/HdrMetadata.h +++ b/libs/gui/include/gui/HdrMetadata.h @@ -17,6 +17,7 @@ #pragma once #include <stdint.h> +#include <vector> #include <system/graphics.h> #include <utils/Flattenable.h> @@ -26,12 +27,15 @@ namespace android { struct HdrMetadata : public LightFlattenable<HdrMetadata> { enum Type : uint32_t { SMPTE2086 = 1 << 0, - CTA861_3 = 1 << 1, + CTA861_3 = 1 << 1, + HDR10PLUS = 1 << 2, }; + uint32_t validTypes{0}; android_smpte2086_metadata smpte2086{}; android_cta861_3_metadata cta8613{}; + std::vector<uint8_t> hdr10plus{}; // LightFlattenable bool isFixedSize() const { return false; } diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 887654e05b..8ff8d81cf6 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -345,7 +345,7 @@ public: *outScalingMode = scalingMode; *outTransform = transform; *outFence = fence; - if (outStickyTransform != NULL) { + if (outStickyTransform != nullptr) { *outStickyTransform = stickyTransform; } if (outGetFrameTimestamps) { diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 99a3a75502..3052c0b312 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -27,10 +27,11 @@ #include <binder/IInterface.h> +#include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> -#include <ui/PixelFormat.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicTypes.h> +#include <ui/PixelFormat.h> #include <vector> @@ -157,9 +158,6 @@ public: virtual status_t getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats) = 0; - /* returns display viewport information of the given display */ - virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) = 0; - /* indicates which of the configurations returned by getDisplayInfo is * currently active */ virtual int getActiveConfig(const sp<IBinder>& display) = 0; @@ -174,21 +172,86 @@ public: virtual status_t setActiveColorMode(const sp<IBinder>& display, ui::ColorMode colorMode) = 0; - /* Capture the specified screen. requires READ_FRAME_BUFFER permission - * This function will fail if there is a secure window on screen. + /** + * Capture the specified screen. This requires READ_FRAME_BUFFER + * permission. This function will fail if there is a secure window on + * screen. + * + * This function can capture a subregion (the source crop) of the screen. + * The subregion can be optionally rotated. It will also be scaled to + * match the size of the output buffer. + * + * reqDataspace and reqPixelFormat specify the data space and pixel format + * of the buffer. The caller should pick the data space and pixel format + * that it can consume. + * + * At the moment, sourceCrop is ignored and is always set to the visible + * region (projected display viewport) of the screen. + * + * reqWidth and reqHeight specifies the size of the buffer. When either + * of them is 0, they are set to the size of the logical display viewport. + * + * When useIdentityTransform is true, layer transformations are disabled. + * + * rotation specifies the rotation of the source crop (and the pixels in + * it) around its center. */ virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, + const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, Rotation rotation = eRotateNone) = 0; + /** + * Capture the specified screen. This requires READ_FRAME_BUFFER + * permission. This function will fail if there is a secure window on + * screen. + * + * This function can capture a subregion (the source crop) of the screen + * into an sRGB buffer with RGBA_8888 pixel format. + * The subregion can be optionally rotated. It will also be scaled to + * match the size of the output buffer. + * + * At the moment, sourceCrop is ignored and is always set to the visible + * region (projected display viewport) of the screen. + * + * reqWidth and reqHeight specifies the size of the buffer. When either + * of them is 0, they are set to the size of the logical display viewport. + * + * When useIdentityTransform is true, layer transformations are disabled. + * + * rotation specifies the rotation of the source crop (and the pixels in + * it) around its center. + */ + virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, + Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, + bool useIdentityTransform, Rotation rotation = eRotateNone) { + return captureScreen(display, outBuffer, ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, + sourceCrop, reqWidth, reqHeight, useIdentityTransform, rotation); + } /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. + * + * reqDataspace and reqPixelFormat specify the data space and pixel format + * of the buffer. The caller should pick the data space and pixel format + * that it can consume. */ virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder, - sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop, + sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, float frameScale = 1.0, bool childrenOnly = false) = 0; + /** + * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format, + * potentially ignoring the root node. + */ + status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, + const Rect& sourceCrop, float frameScale = 1.0, + bool childrenOnly = false) { + return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, sourceCrop, frameScale, childrenOnly); + } + /* Clears the frame statistics for animations. * * Requires the ACCESS_SURFACE_FLINGER permission. @@ -217,18 +280,55 @@ public: * Requires the ACCESS_SURFACE_FLINGER permission. */ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0; + + virtual status_t getColorManagement(bool* outGetColorManagement) const = 0; + + /* Gets the composition preference of the default data space and default pixel format, + * as well as the wide color gamut data space and wide color gamut pixel format. + * If the wide color gamut data space is V0_SRGB, then it implies that the platform + * has no wide color gamut support. + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace, + ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, + ui::PixelFormat* wideColorGamutPixelFormat) const = 0; + /* + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display, + ui::PixelFormat* outFormat, + ui::Dataspace* outDataspace, + uint8_t* outComponentMask) const = 0; + + /* Turns on the color sampling engine on the display. + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, + uint8_t componentMask, + uint64_t maxFrames) const = 0; + + /* Returns statistics on the color profile of the last frame displayed for a given display + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, + uint64_t timestamp, + DisplayedFrameStats* outStats) const = 0; }; // ---------------------------------------------------------------------------- class BnSurfaceComposer: public BnInterface<ISurfaceComposer> { public: - enum { + enum ISurfaceComposerTag { // Note: BOOT_FINISHED must remain this value, it is called from // Java by ActivityManagerService. BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION, CREATE_CONNECTION, - UNUSED, // formerly CREATE_GRAPHIC_BUFFER_ALLOC + CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED, // unused, fails permissions check CREATE_DISPLAY_EVENT_CONNECTION, CREATE_DISPLAY, DESTROY_DISPLAY, @@ -239,7 +339,7 @@ public: GET_DISPLAY_CONFIGS, GET_ACTIVE_CONFIG, SET_ACTIVE_CONFIG, - CONNECT_DISPLAY, + CONNECT_DISPLAY_UNUSED, // unused, fails permissions check CAPTURE_SCREEN, CAPTURE_LAYERS, CLEAR_ANIMATION_FRAME_STATS, @@ -254,7 +354,11 @@ public: INJECT_VSYNC, GET_LAYER_DEBUG_INFO, CREATE_SCOPED_CONNECTION, - GET_DISPLAY_VIEWPORT + GET_COMPOSITION_PREFERENCE, + GET_COLOR_MANAGEMENT, + GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES, + SET_DISPLAY_CONTENT_SAMPLING_ENABLED, + GET_DISPLAYED_CONTENT_SAMPLE, }; virtual status_t onTransact(uint32_t code, const Parcel& data, diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h index b4fd956b0f..82b01b8cf0 100644 --- a/libs/gui/include/gui/ISurfaceComposerClient.h +++ b/libs/gui/include/gui/ISurfaceComposerClient.h @@ -40,8 +40,9 @@ public: eProtectedByDRM = 0x00001000, eCursorWindow = 0x00002000, - eFXSurfaceNormal = 0x00000000, + eFXSurfaceBufferQueue = 0x00000000, eFXSurfaceColor = 0x00020000, + eFXSurfaceBufferState = 0x00040000, eFXSurfaceContainer = 0x00080000, eFXSurfaceMask = 0x000F0000, }; diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h new file mode 100644 index 0000000000..8acfa7a8f6 --- /dev/null +++ b/libs/gui/include/gui/ITransactionCompletedListener.h @@ -0,0 +1,116 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <binder/SafeInterface.h> + +#include <ui/Fence.h> +#include <utils/Timers.h> + +#include <cstdint> +#include <unordered_map> +#include <unordered_set> + +namespace android { + +class ITransactionCompletedListener; + +using CallbackId = int64_t; + +struct CallbackIdsHash { + // CallbackId vectors have several properties that let us get away with this simple hash. + // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is + // empty we can still hash 0. + // 2) CallbackId vectors for the same listener either are identical or contain none of the + // same members. It is sufficient to just check the first CallbackId in the vectors. If + // they match, they are the same. If they do not match, they are not the same. + std::size_t operator()(const std::vector<CallbackId> callbackIds) const { + return std::hash<CallbackId>{}((callbackIds.size() == 0) ? 0 : callbackIds.front()); + } +}; + +class SurfaceStats : public Parcelable { +public: + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + SurfaceStats() = default; + SurfaceStats(const sp<IBinder>& sc, nsecs_t time, bool releasePrevBuffer) + : surfaceControl(sc), acquireTime(time), releasePreviousBuffer(releasePrevBuffer) {} + + sp<IBinder> surfaceControl; + nsecs_t acquireTime = -1; + bool releasePreviousBuffer = false; +}; + +class TransactionStats : public Parcelable { +public: + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + nsecs_t latchTime = -1; + sp<Fence> presentFence = nullptr; + std::vector<SurfaceStats> surfaceStats; +}; + +class ListenerStats : public Parcelable { +public: + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + static ListenerStats createEmpty(const sp<ITransactionCompletedListener>& listener, + const std::unordered_set<CallbackId>& callbackIds); + + sp<ITransactionCompletedListener> listener; + std::unordered_map<std::vector<CallbackId>, TransactionStats, CallbackIdsHash> transactionStats; +}; + +class ITransactionCompletedListener : public IInterface { +public: + DECLARE_META_INTERFACE(TransactionCompletedListener) + + virtual void onTransactionCompleted(ListenerStats stats) = 0; +}; + +class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> { +public: + BnTransactionCompletedListener() + : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {} + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0) override; +}; + +class ListenerCallbacks { +public: + ListenerCallbacks(const sp<ITransactionCompletedListener>& listener, + const std::unordered_set<CallbackId>& callbacks) + : transactionCompletedListener(listener), + callbackIds(callbacks.begin(), callbacks.end()) {} + + ListenerCallbacks(const sp<ITransactionCompletedListener>& listener, + const std::vector<CallbackId>& ids) + : transactionCompletedListener(listener), callbackIds(ids) {} + + sp<ITransactionCompletedListener> transactionCompletedListener; + std::vector<CallbackId> callbackIds; +}; + +} // namespace android diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h index 92bd8c5b28..66a7b4dc06 100644 --- a/libs/gui/include/gui/LayerDebugInfo.h +++ b/libs/gui/include/gui/LayerDebugInfo.h @@ -52,7 +52,6 @@ public: int32_t mWidth = -1; int32_t mHeight = -1; Rect mCrop = Rect::INVALID_RECT; - Rect mFinalCrop = Rect::INVALID_RECT; half4 mColor = half4(1.0_hf, 1.0_hf, 1.0_hf, 0.0_hf); uint32_t mFlags = 0; PixelFormat mPixelFormat = PIXEL_FORMAT_NONE; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 788962e490..02c6be25c7 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -22,10 +22,18 @@ #include <utils/Errors.h> -#include <ui/Region.h> -#include <ui/Rect.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/ITransactionCompletedListener.h> +#include <math/mat4.h> + +#ifndef NO_INPUT +#include <input/InputWindow.h> +#endif + #include <math/vec3.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> namespace android { @@ -36,113 +44,159 @@ class ISurfaceComposerClient; * Used to communicate layer information between SurfaceFlinger and its clients. */ struct layer_state_t { - - enum { - eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java - eLayerOpaque = 0x02, // SURFACE_OPAQUE - eLayerSecure = 0x80, // SECURE + eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java + eLayerOpaque = 0x02, // SURFACE_OPAQUE + eLayerSecure = 0x80, // SECURE }; enum { - ePositionChanged = 0x00000001, - eLayerChanged = 0x00000002, - eSizeChanged = 0x00000004, - eAlphaChanged = 0x00000008, - eMatrixChanged = 0x00000010, - eTransparentRegionChanged = 0x00000020, - eFlagsChanged = 0x00000040, - eLayerStackChanged = 0x00000080, - eCropChanged = 0x00000100, - eDeferTransaction = 0x00000200, - eFinalCropChanged = 0x00000400, - eOverrideScalingModeChanged = 0x00000800, - eGeometryAppliesWithResize = 0x00001000, - eReparentChildren = 0x00002000, - eDetachChildren = 0x00004000, - eRelativeLayerChanged = 0x00008000, - eReparent = 0x00010000, - eColorChanged = 0x00020000, - eDestroySurface = 0x00040000 + ePositionChanged = 0x00000001, + eLayerChanged = 0x00000002, + eSizeChanged = 0x00000004, + eAlphaChanged = 0x00000008, + eMatrixChanged = 0x00000010, + eTransparentRegionChanged = 0x00000020, + eFlagsChanged = 0x00000040, + eLayerStackChanged = 0x00000080, + eCropChanged_legacy = 0x00000100, + eDeferTransaction_legacy = 0x00000200, + eOverrideScalingModeChanged = 0x00000400, + eGeometryAppliesWithResize = 0x00000800, + eReparentChildren = 0x00001000, + eDetachChildren = 0x00002000, + eRelativeLayerChanged = 0x00004000, + eReparent = 0x00008000, + eColorChanged = 0x00010000, + eDestroySurface = 0x00020000, + eTransformChanged = 0x00040000, + eTransformToDisplayInverseChanged = 0x00080000, + eCropChanged = 0x00100000, + eBufferChanged = 0x00200000, + eAcquireFenceChanged = 0x00400000, + eDataspaceChanged = 0x00800000, + eHdrMetadataChanged = 0x01000000, + eSurfaceDamageRegionChanged = 0x02000000, + eApiChanged = 0x04000000, + eSidebandStreamChanged = 0x08000000, + eColorTransformChanged = 0x10000000, + eListenerCallbacksChanged = 0x20000000, + eInputInfoChanged = 0x40000000, + eCornerRadiusChanged = 0x80000000, + eFrameChanged = 0x1'00000000, }; layer_state_t() - : what(0), - x(0), y(0), z(0), w(0), h(0), layerStack(0), - alpha(0), flags(0), mask(0), - reserved(0), crop(Rect::INVALID_RECT), - finalCrop(Rect::INVALID_RECT), frameNumber(0), - overrideScalingMode(-1) - { + : what(0), + x(0), + y(0), + z(0), + w(0), + h(0), + layerStack(0), + alpha(0), + flags(0), + mask(0), + reserved(0), + crop_legacy(Rect::INVALID_RECT), + cornerRadius(0.0f), + frameNumber_legacy(0), + overrideScalingMode(-1), + transform(0), + transformToDisplayInverse(false), + crop(Rect::INVALID_RECT), + frame(Rect::INVALID_RECT), + dataspace(ui::Dataspace::UNKNOWN), + surfaceDamageRegion(), + api(-1), + colorTransform(mat4()) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; + hdrMetadata.validTypes = 0; } void merge(const layer_state_t& other); - status_t write(Parcel& output) const; - status_t read(const Parcel& input); - - struct matrix22_t { - float dsdx{0}; - float dtdx{0}; - float dtdy{0}; - float dsdy{0}; - }; - sp<IBinder> surface; - uint32_t what; - float x; - float y; - int32_t z; - uint32_t w; - uint32_t h; - uint32_t layerStack; - float alpha; - uint8_t flags; - uint8_t mask; - uint8_t reserved; - matrix22_t matrix; - Rect crop; - Rect finalCrop; - sp<IBinder> barrierHandle; - sp<IBinder> reparentHandle; - uint64_t frameNumber; - int32_t overrideScalingMode; - - sp<IGraphicBufferProducer> barrierGbp; - - sp<IBinder> relativeLayerHandle; - - sp<IBinder> parentHandleForChild; - - half3 color; - - // non POD must be last. see write/read - Region transparentRegion; + status_t write(Parcel& output) const; + status_t read(const Parcel& input); + + struct matrix22_t { + float dsdx{0}; + float dtdx{0}; + float dtdy{0}; + float dsdy{0}; + }; + sp<IBinder> surface; + uint64_t what; + float x; + float y; + int32_t z; + uint32_t w; + uint32_t h; + uint32_t layerStack; + float alpha; + uint8_t flags; + uint8_t mask; + uint8_t reserved; + matrix22_t matrix; + Rect crop_legacy; + float cornerRadius; + sp<IBinder> barrierHandle_legacy; + sp<IBinder> reparentHandle; + uint64_t frameNumber_legacy; + int32_t overrideScalingMode; + + sp<IGraphicBufferProducer> barrierGbp_legacy; + + sp<IBinder> relativeLayerHandle; + + sp<IBinder> parentHandleForChild; + + half3 color; + + // non POD must be last. see write/read + Region transparentRegion; + + uint32_t transform; + bool transformToDisplayInverse; + Rect crop; + Rect frame; + sp<GraphicBuffer> buffer; + sp<Fence> acquireFence; + ui::Dataspace dataspace; + HdrMetadata hdrMetadata; + Region surfaceDamageRegion; + int32_t api; + sp<NativeHandle> sidebandStream; + mat4 colorTransform; + + std::vector<ListenerCallbacks> listenerCallbacks; +#ifndef NO_INPUT + InputWindowInfo inputInfo; +#endif }; struct ComposerState { sp<ISurfaceComposerClient> client; layer_state_t state; - status_t write(Parcel& output) const; - status_t read(const Parcel& input); + status_t write(Parcel& output) const; + status_t read(const Parcel& input); }; struct DisplayState { - enum { - eOrientationDefault = 0, - eOrientation90 = 1, - eOrientation180 = 2, - eOrientation270 = 3, - eOrientationUnchanged = 4, - eOrientationSwapMask = 0x01 + eOrientationDefault = 0, + eOrientation90 = 1, + eOrientation180 = 2, + eOrientation270 = 3, + eOrientationUnchanged = 4, + eOrientationSwapMask = 0x01 }; enum { - eSurfaceChanged = 0x01, - eLayerStackChanged = 0x02, - eDisplayProjectionChanged = 0x04, - eDisplaySizeChanged = 0x08 + eSurfaceChanged = 0x01, + eLayerStackChanged = 0x02, + eDisplayProjectionChanged = 0x04, + eDisplaySizeChanged = 0x08 }; DisplayState(); @@ -152,29 +206,40 @@ struct DisplayState { sp<IBinder> token; sp<IGraphicBufferProducer> surface; uint32_t layerStack; + + // These states define how layers are projected onto the physical display. + // + // Layers are first clipped to `viewport'. They are then translated and + // scaled from `viewport' to `frame'. Finally, they are rotated according + // to `orientation', `width', and `height'. + // + // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20, + // 10, 420, 210), and the size of the display is WxH. When orientation is + // 0, layers will be scaled by a factor of 2 and translated by (20, 10). + // When orientation is 1, layers will be additionally rotated by 90 + // degrees around the origin clockwise and translated by (W, 0). uint32_t orientation; Rect viewport; Rect frame; + uint32_t width, height; + status_t write(Parcel& output) const; status_t read(const Parcel& input); }; -static inline -int compare_type(const ComposerState& lhs, const ComposerState& rhs) { +static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) { if (lhs.client < rhs.client) return -1; if (lhs.client > rhs.client) return 1; - if (lhs.state.surface < rhs.state.surface) return -1; - if (lhs.state.surface > rhs.state.surface) return 1; + if (lhs.state.surface < rhs.state.surface) return -1; + if (lhs.state.surface > rhs.state.surface) return 1; return 0; } -static inline -int compare_type(const DisplayState& lhs, const DisplayState& rhs) { +static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) { return compare_type(lhs.token, rhs.token); } }; // namespace android #endif // ANDROID_SF_LAYER_STATE_H - diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 9aeafae198..248e105d04 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -80,7 +80,7 @@ public: /* convenience function to check that the given surface is non NULL as * well as its IGraphicBufferProducer */ static bool isValid(const sp<Surface>& surface) { - return surface != NULL && surface->getIGraphicBufferProducer() != NULL; + return surface != nullptr && surface->getIGraphicBufferProducer() != nullptr; } /* Attaches a sideband buffer stream to the Surface's IGraphicBufferProducer. @@ -218,6 +218,7 @@ private: int dispatchSetBuffersDataSpace(va_list args); int dispatchSetBuffersSmpte2086Metadata(va_list args); int dispatchSetBuffersCta8613Metadata(va_list args); + int dispatchSetBuffersHdr10PlusMetadata(va_list args); int dispatchSetSurfaceDamage(va_list args); int dispatchSetSharedBufferMode(va_list args); int dispatchSetAutoRefresh(va_list args); @@ -249,6 +250,7 @@ protected: virtual int setBuffersDataSpace(ui::Dataspace dataSpace); virtual int setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata); virtual int setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata); + virtual int setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata); virtual int setCrop(Rect const* rect); virtual int setUsage(uint64_t reqUsage); virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects); diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index ad8a8b09d0..8e3ba78108 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -19,7 +19,9 @@ #include <stdint.h> #include <sys/types.h> +#include <set> #include <unordered_map> +#include <unordered_set> #include <binder/IBinder.h> @@ -28,14 +30,16 @@ #include <utils/SortedVector.h> #include <utils/threads.h> +#include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> #include <ui/GraphicTypes.h> #include <ui/PixelFormat.h> #include <gui/CpuConsumer.h> +#include <gui/ITransactionCompletedListener.h> +#include <gui/LayerState.h> #include <gui/SurfaceControl.h> #include <math/vec3.h> -#include <gui/LayerState.h> namespace android { @@ -49,6 +53,37 @@ class Region; // --------------------------------------------------------------------------- +using TransactionCompletedCallbackTakesContext = + std::function<void(void* /*context*/, const TransactionStats&)>; +using TransactionCompletedCallback = std::function<void(const TransactionStats&)>; + +class TransactionCompletedListener : public BnTransactionCompletedListener { + TransactionCompletedListener(); + + CallbackId getNextIdLocked() REQUIRES(mMutex); + + std::mutex mMutex; + + bool mListening GUARDED_BY(mMutex) = false; + + CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1; + + std::map<CallbackId, TransactionCompletedCallback> mCallbacks GUARDED_BY(mMutex); + +public: + static sp<TransactionCompletedListener> getInstance(); + static sp<ITransactionCompletedListener> getIInstance(); + + void startListeningLocked() REQUIRES(mMutex); + + CallbackId addCallback(const TransactionCompletedCallback& callback); + + // Overrides BnTransactionCompletedListener's onTransactionCompleted + void onTransactionCompleted(ListenerStats stats) override; +}; + +// --------------------------------------------------------------------------- + class SurfaceComposerClient : public RefBase { friend class Composer; @@ -69,7 +104,7 @@ public: // callback when the composer is dies status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient, - void* cookie = NULL, uint32_t flags = 0); + void* cookie = nullptr, uint32_t flags = 0); // Get a list of supported configurations for a given display static status_t getDisplayConfigs(const sp<IBinder>& display, @@ -79,9 +114,6 @@ public: static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info); - // Get the display viewport for the given display - static status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport); - // Get the index of the current active configuration (relative to the list // returned by getDisplayInfo) static int getActiveConfig(const sp<IBinder>& display); @@ -104,6 +136,16 @@ public: /* Triggers screen on/off or low power mode and waits for it to complete */ static void setDisplayPowerMode(const sp<IBinder>& display, int mode); + /* Returns the composition preference of the default data space and default pixel format, + * as well as the wide color gamut data space and wide color gamut pixel format. + * If the wide color gamut data space is V0_SRGB, then it implies that the platform + * has no wide color gamut support. + */ + static status_t getCompositionPreference(ui::Dataspace* defaultDataspace, + ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, + ui::PixelFormat* wideColorGamutPixelFormat); + // ------------------------------------------------------------------------ // surface creation / destruction @@ -151,9 +193,27 @@ public: } }; + struct TCLHash { + std::size_t operator()(const sp<ITransactionCompletedListener>& tcl) const { + return std::hash<IBinder*>{}((tcl) ? IInterface::asBinder(tcl).get() : nullptr); + } + }; + + struct CallbackInfo { + // All the callbacks that have been requested for a TransactionCompletedListener in the + // Transaction + std::unordered_set<CallbackId> callbackIds; + // All the SurfaceControls that have been modified in this TransactionCompletedListener's + // process that require a callback if there is one or more callbackIds set. + std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls; + }; + class Transaction { std::unordered_map<sp<SurfaceControl>, ComposerState, SCHash> mComposerStates; SortedVector<DisplayState > mDisplayStates; + std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> + mListenerCallbacks; + uint32_t mForceSynchronous = 0; uint32_t mTransactionNestCount = 0; bool mAnimation = false; @@ -164,6 +224,8 @@ public: layer_state_t* getLayerState(const sp<SurfaceControl>& sc); DisplayState& getDisplayState(const sp<IBinder>& token); + void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc); + public: Transaction() = default; virtual ~Transaction() = default; @@ -203,22 +265,21 @@ public: float alpha); Transaction& setMatrix(const sp<SurfaceControl>& sc, float dsdx, float dtdx, float dtdy, float dsdy); - Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop); - Transaction& setFinalCrop(const sp<SurfaceControl>& sc, const Rect& crop); + Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop); + Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius); Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack); // Defers applying any changes made in this transaction until the Layer // identified by handle reaches the given frameNumber. If the Layer identified // by handle is removed, then we will apply this transaction regardless of // what frame number has been reached. - Transaction& deferTransactionUntil(const sp<SurfaceControl>& sc, - const sp<IBinder>& handle, - uint64_t frameNumber); - // A variant of deferTransactionUntil which identifies the Layer we wait for by + Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<IBinder>& handle, uint64_t frameNumber); + // A variant of deferTransactionUntil_legacy which identifies the Layer we wait for by // Surface instead of Handle. Useful for clients which may not have the // SurfaceControl for some of their Surfaces. Otherwise behaves identically. - Transaction& deferTransactionUntil(const sp<SurfaceControl>& sc, - const sp<Surface>& barrierSurface, - uint64_t frameNumber); + Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<Surface>& barrierSurface, + uint64_t frameNumber); // Reparents all children of this layer to the new parent handle. Transaction& reparentChildren(const sp<SurfaceControl>& sc, const sp<IBinder>& newParentHandle); @@ -231,6 +292,24 @@ public: Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color); + Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform); + Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc, + bool transformToDisplayInverse); + Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop); + Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame); + Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer); + Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence); + Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace); + Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata); + Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc, + const Region& surfaceDamageRegion); + Transaction& setApi(const sp<SurfaceControl>& sc, int32_t api); + Transaction& setSidebandStream(const sp<SurfaceControl>& sc, + const sp<NativeHandle>& sidebandStream); + + Transaction& addTransactionCompletedCallback( + TransactionCompletedCallbackTakesContext callback, void* callbackContext); + // Detaches all child surfaces (and their children recursively) // from their SurfaceControl. // The child SurfaceControls will not throw exceptions or return errors, @@ -254,8 +333,16 @@ public: // freezing the total geometry of a surface until a resize is completed. Transaction& setGeometryAppliesWithResize(const sp<SurfaceControl>& sc); +#ifndef NO_INPUT + Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info); +#endif + Transaction& destroySurface(const sp<SurfaceControl>& sc); + // Set a color transform matrix on the given layer on the built-in display. + Transaction& setColorTransform(const sp<SurfaceControl>& sc, const mat3& matrix, + const vec3& translation); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -297,6 +384,16 @@ public: inline sp<ISurfaceComposerClient> getClient() { return mClient; } + static status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display, + ui::PixelFormat* outFormat, + ui::Dataspace* outDataspace, + uint8_t* outComponentMask); + static status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, + uint8_t componentMask, uint64_t maxFrames); + + static status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, + uint64_t timestamp, DisplayedFrameStats* outStats); + private: virtual void onFirstRef(); @@ -312,17 +409,21 @@ class ScreenshotClient { public: // if cropping isn't required, callers may pass in a default Rect, e.g.: // capture(display, producer, Rect(), reqWidth, ...); - static status_t capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, - bool useIdentityTransform, uint32_t rotation, - sp<GraphicBuffer>* outBuffer); - static status_t captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, float frameScale, - sp<GraphicBuffer>* outBuffer); - static status_t captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, + static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + uint32_t rotation, sp<GraphicBuffer>* outBuffer); + static status_t captureLayers(const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + float frameScale, sp<GraphicBuffer>* outBuffer); + static status_t captureChildLayers(const sp<IBinder>& layerHandle, + const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, float frameScale, sp<GraphicBuffer>* outBuffer); }; // --------------------------------------------------------------------------- + }; // namespace android #endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index bd987dd638..ccb30fa8e7 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -48,11 +48,11 @@ public: void writeToParcel(Parcel* parcel); static bool isValid(const sp<SurfaceControl>& surface) { - return (surface != 0) && surface->isValid(); + return (surface != nullptr) && surface->isValid(); } bool isValid() { - return mHandle!=0 && mClient!=0; + return mHandle!=nullptr && mClient!=nullptr; } static bool isSameSurface( diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 01e90e0eb8..f020a4067d 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -16,6 +16,8 @@ cc_test { "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "CpuConsumer_test.cpp", + "EndToEndNativeInputTest.cpp", + "DisplayedContentSampling_test.cpp", "FillBuffer.cpp", "GLTest.cpp", "IGraphicBufferProducer_test.cpp", @@ -35,6 +37,7 @@ cc_test { shared_libs: [ "android.hardware.configstore@1.0", "android.hardware.configstore-utils", + "libbase", "liblog", "libEGL", "libGLESv1_CM", @@ -44,15 +47,19 @@ cc_test { "libgui", "libhidlbase", "libhidltransport", + "libinput", "libui", "libutils", "libnativewindow" ], } -// Build a separate binary for each source file to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +// This test has a main method, and requires a separate binary to be built. +// To add move tests like this, just add additional cc_test statements, +// as opposed to adding more source files to this one. cc_test { - name: "libgui_separate_binary_test", + name: "SurfaceParcelable_test", test_suites: ["device-tests"], clang: true, @@ -61,7 +68,6 @@ cc_test { "-Werror", ], - test_per_src: true, srcs: [ "SurfaceParcelable_test.cpp", ], diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 9a208593ab..119e888edb 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -61,7 +61,7 @@ protected: } void GetMinUndequeuedBufferCount(int* bufferCount) { - ASSERT_TRUE(bufferCount != NULL); + ASSERT_TRUE(bufferCount != nullptr); ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, bufferCount)); ASSERT_GE(*bufferCount, 0); @@ -82,7 +82,7 @@ protected: sp<Fence> fence; input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, - &scalingMode, &transform, &fence, NULL); + &scalingMode, &transform, &fence, nullptr); ASSERT_EQ(timestamp, item.mTimestamp); ASSERT_EQ(isAutoTimestamp, item.mIsAutoTimestamp); ASSERT_EQ(dataSpace, item.mDataSpace); @@ -128,17 +128,17 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { sp<IBinder> binderProducer = serviceManager->getService(PRODUCER_NAME); mProducer = interface_cast<IGraphicBufferProducer>(binderProducer); - EXPECT_TRUE(mProducer != NULL); + EXPECT_TRUE(mProducer != nullptr); sp<IBinder> binderConsumer = serviceManager->getService(CONSUMER_NAME); mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer); - EXPECT_TRUE(mConsumer != NULL); + EXPECT_TRUE(mConsumer != nullptr); sp<DummyConsumer> dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, - mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output)); + mProducer->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output)); int slot; sp<Fence> fence; @@ -353,8 +353,8 @@ TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) { ASSERT_EQ(OK, buffer->unlock()); int newSlot; - ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(NULL, safeToClobberBuffer)); - ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, NULL)); + ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(nullptr, safeToClobberBuffer)); + ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, nullptr)); ASSERT_EQ(OK, mProducer->attachBuffer(&newSlot, buffer)); IGraphicBufferProducer::QueueBufferInput input(0, false, @@ -412,8 +412,8 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { int newSlot; sp<GraphicBuffer> safeToClobberBuffer; - ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(NULL, safeToClobberBuffer)); - ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, NULL)); + ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(nullptr, safeToClobberBuffer)); + ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, nullptr)); ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer)); ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, EGL_NO_DISPLAY, diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp index 36be7d9368..00e32d9124 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -484,12 +484,12 @@ void produceOneFrame(const sp<ANativeWindow>& anw, err = native_window_dequeue_buffer_and_wait(anw.get(), &anb); ASSERT_NO_ERROR(err, "dequeueBuffer error: "); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); *stride = buf->getStride(); - uint8_t* img = NULL; + uint8_t* img = nullptr; ALOGVV("Lock buffer from %p for write", anw.get()); err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); @@ -554,7 +554,7 @@ TEST_P(CpuConsumerTest, FromCpuSingle) { err = mCC->lockNextBuffer(&b); ASSERT_NO_ERROR(err, "getNextBuffer error: "); - ASSERT_TRUE(b.data != NULL); + ASSERT_TRUE(b.data != nullptr); EXPECT_EQ(params.width, b.width); EXPECT_EQ(params.height, b.height); EXPECT_EQ(params.format, b.format); @@ -595,7 +595,7 @@ TEST_P(CpuConsumerTest, FromCpuManyInQueue) { err = mCC->lockNextBuffer(&b); ASSERT_NO_ERROR(err, "getNextBuffer error: "); - ASSERT_TRUE(b.data != NULL); + ASSERT_TRUE(b.data != nullptr); EXPECT_EQ(params.width, b.width); EXPECT_EQ(params.height, b.height); EXPECT_EQ(params.format, b.format); @@ -637,7 +637,7 @@ TEST_P(CpuConsumerTest, FromCpuLockMax) { err = mCC->lockNextBuffer(&b[i]); ASSERT_NO_ERROR(err, "getNextBuffer error: "); - ASSERT_TRUE(b[i].data != NULL); + ASSERT_TRUE(b[i].data != nullptr); EXPECT_EQ(params.width, b[i].width); EXPECT_EQ(params.height, b[i].height); EXPECT_EQ(params.format, b[i].format); @@ -660,7 +660,7 @@ TEST_P(CpuConsumerTest, FromCpuLockMax) { err = mCC->lockNextBuffer(&bTooMuch); ASSERT_NO_ERROR(err, "Did not allow new lock after unlock"); - ASSERT_TRUE(bTooMuch.data != NULL); + ASSERT_TRUE(bTooMuch.data != nullptr); EXPECT_EQ(params.width, bTooMuch.width); EXPECT_EQ(params.height, bTooMuch.height); EXPECT_EQ(params.format, bTooMuch.format); diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp new file mode 100644 index 0000000000..5443812bff --- /dev/null +++ b/libs/gui/tests/DisplayedContentSampling_test.cpp @@ -0,0 +1,122 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <binder/ProcessState.h> +#include <gui/ISurfaceComposer.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <inttypes.h> + +namespace android { + +using Transaction = SurfaceComposerClient::Transaction; + +static constexpr uint32_t INVALID_MASK = 0x10; +class DisplayedContentSamplingTest : public ::testing::Test { +protected: + void SetUp() { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(OK, mComposerClient->initCheck()); + mDisplayToken = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain); + ASSERT_TRUE(mDisplayToken); + } + + bool shouldSkipTest() { + ui::PixelFormat format; + ui::Dataspace dataspace; + status_t status = + mComposerClient->getDisplayedContentSamplingAttributes(mDisplayToken, &format, + &dataspace, &componentMask); + if (status == PERMISSION_DENIED) { + SUCCEED() << "permissions denial, skipping test"; + return true; + } + if (status == INVALID_OPERATION) { + SUCCEED() << "optional function not supported, skipping test"; + return true; + } + return false; + } + + sp<SurfaceComposerClient> mComposerClient; + sp<IBinder> mDisplayToken; + uint8_t componentMask = 0; +}; + +TEST_F(DisplayedContentSamplingTest, GetDisplayedContentSamplingAttributesAreSane) { + // tradefed infrastructure does not support use of GTEST_SKIP + if (shouldSkipTest()) return; + + ui::PixelFormat format; + ui::Dataspace dataspace; + status_t status = + mComposerClient->getDisplayedContentSamplingAttributes(mDisplayToken, &format, + &dataspace, &componentMask); + EXPECT_EQ(OK, status); + EXPECT_LE(componentMask, INVALID_MASK); +} + +TEST_F(DisplayedContentSamplingTest, EnableWithInvalidMaskReturnsBadValue) { + if (shouldSkipTest()) return; + + status_t status = + mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true, INVALID_MASK, 0); + EXPECT_EQ(BAD_VALUE, status); +} + +TEST_F(DisplayedContentSamplingTest, EnableAndDisableSucceed) { + if (shouldSkipTest()) return; + + status_t status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true, + componentMask, 10); + EXPECT_EQ(OK, status); + + status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, false, componentMask, + 0); + EXPECT_EQ(OK, status); +} + +TEST_F(DisplayedContentSamplingTest, SelectivelyDisableComponentOk) { + if (shouldSkipTest()) return; + + status_t status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true, + componentMask, 0); + EXPECT_EQ(OK, status); + + // Clear the lowest bit. + componentMask &= (componentMask - 1); + status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, false, componentMask, + 0); + EXPECT_EQ(OK, status); +} + +TEST_F(DisplayedContentSamplingTest, SampleCollectionCoherentWithSupportMask) { + if (shouldSkipTest()) return; + + DisplayedFrameStats stats; + status_t status = mComposerClient->getDisplayedContentSample(mDisplayToken, 0, 0, &stats); + EXPECT_EQ(OK, status); + if (stats.numFrames <= 0) return; + + if (componentMask & (0x1 << 0)) EXPECT_NE(0, stats.component_0_sample.size()); + if (componentMask & (0x1 << 1)) EXPECT_NE(0, stats.component_1_sample.size()); + if (componentMask & (0x1 << 2)) EXPECT_NE(0, stats.component_2_sample.size()); + if (componentMask & (0x1 << 3)) EXPECT_NE(0, stats.component_3_sample.size()); +} + +} // namespace android diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp new file mode 100644 index 0000000000..60542bd47c --- /dev/null +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -0,0 +1,443 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/types.h> +#include <stdio.h> +#include <poll.h> + +#include <memory> + +#include <android/native_window.h> + +#include <binder/Binder.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/ProcessState.h> + +#include <gui/ISurfaceComposer.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/SurfaceControl.h> + +#include <input/InputWindow.h> +#include <input/IInputFlinger.h> +#include <input/InputTransport.h> +#include <input/Input.h> + +#include <ui/DisplayInfo.h> +#include <ui/Rect.h> +#include <ui/Region.h> + + +namespace android { +namespace test { + +using Transaction = SurfaceComposerClient::Transaction; + +sp<IInputFlinger> getInputFlinger() { + sp<IBinder> input(defaultServiceManager()->getService( + String16("inputflinger"))); + if (input == nullptr) { + ALOGE("Failed to link to input service"); + } else { ALOGE("Linked to input"); } + return interface_cast<IInputFlinger>(input); +} + +// We use the top 10 layers as a way to haphazardly place ourselves above anything else. +static const int LAYER_BASE = INT32_MAX - 10; + +class InputSurface { +public: + InputSurface(const sp<SurfaceControl> &sc, int width, int height) { + mSurfaceControl = sc; + + InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel); + mServerChannel->setToken(new BBinder()); + + mInputFlinger = getInputFlinger(); + mInputFlinger->registerInputChannel(mServerChannel); + + populateInputInfo(width, height); + + mInputConsumer = new InputConsumer(mClientChannel); + } + + static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient> &scc, + int width, int height) { + sp<SurfaceControl> surfaceControl = + scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */, + PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor); + return std::make_unique<InputSurface>(surfaceControl, width, height); + } + + static std::unique_ptr<InputSurface> makeBufferInputSurface( + const sp<SurfaceComposerClient> &scc, int width, int height) { + sp<SurfaceControl> surfaceControl = + scc->createSurface(String8("Test Buffer Surface"), width, height, + PIXEL_FORMAT_RGBA_8888, 0 /* flags */); + return std::make_unique<InputSurface>(surfaceControl, width, height); + } + + static std::unique_ptr<InputSurface> makeContainerInputSurface( + const sp<SurfaceComposerClient> &scc, int width, int height) { + sp<SurfaceControl> surfaceControl = + scc->createSurface(String8("Test Container Surface"), 0 /* bufHeight */, + 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceContainer); + return std::make_unique<InputSurface>(surfaceControl, width, height); + } + + InputEvent* consumeEvent() { + waitForEventAvailable(); + + InputEvent *ev; + uint32_t seqId; + status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev); + if (consumed != OK) { + return nullptr; + } + mInputConsumer->sendFinishedSignal(seqId, true); + return ev; + } + + void expectTap(int x, int y) { + InputEvent* ev = consumeEvent(); + EXPECT_TRUE(ev != nullptr); + EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION); + MotionEvent* mev = static_cast<MotionEvent*>(ev); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction()); + EXPECT_EQ(x, mev->getX(0)); + EXPECT_EQ(y, mev->getY(0)); + + ev = consumeEvent(); + EXPECT_TRUE(ev != nullptr); + EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION); + mev = static_cast<MotionEvent*>(ev); + EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction()); + } + + ~InputSurface() { + mInputFlinger->unregisterInputChannel(mServerChannel); + } + + void doTransaction(std::function<void(SurfaceComposerClient::Transaction&, + const sp<SurfaceControl>&)> transactionBody) { + SurfaceComposerClient::Transaction t; + transactionBody(t, mSurfaceControl); + t.apply(true); + } + + void showAt(int x, int y) { + SurfaceComposerClient::Transaction t; + t.show(mSurfaceControl); + t.setInputWindowInfo(mSurfaceControl, mInputInfo); + t.setLayer(mSurfaceControl, LAYER_BASE); + t.setPosition(mSurfaceControl, x, y); + t.setCrop_legacy(mSurfaceControl, Rect(0, 0, 100, 100)); + t.setAlpha(mSurfaceControl, 1); + t.apply(true); + } + +private: + void waitForEventAvailable() { + struct pollfd fd; + + fd.fd = mClientChannel->getFd(); + fd.events = POLLIN; + poll(&fd, 1, 3000); + } + + void populateInputInfo(int width, int height) { + mInputInfo.token = mServerChannel->getToken(); + mInputInfo.name = "Test info"; + mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL; + mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION; + mInputInfo.dispatchingTimeout = 100000; + mInputInfo.globalScaleFactor = 1.0; + mInputInfo.canReceiveKeys = true; + mInputInfo.hasFocus = true; + mInputInfo.hasWallpaper = false; + mInputInfo.paused = false; + + mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height)); + + // TODO: Fill in from SF? + mInputInfo.ownerPid = 11111; + mInputInfo.ownerUid = 11111; + mInputInfo.inputFeatures = 0; + mInputInfo.displayId = 0; + + InputApplicationInfo aInfo; + aInfo.token = new BBinder(); + aInfo.name = "Test app info"; + aInfo.dispatchingTimeout = 100000; + + mInputInfo.applicationInfo = aInfo; + } +public: + sp<SurfaceControl> mSurfaceControl; + sp<InputChannel> mServerChannel, mClientChannel; + sp<IInputFlinger> mInputFlinger; + + InputWindowInfo mInputInfo; + + PreallocatedInputEventFactory mInputEventFactory; + InputConsumer* mInputConsumer; +}; + +class InputSurfacesTest : public ::testing::Test { +public: + InputSurfacesTest() { + ProcessState::self()->startThreadPool(); + } + + void SetUp() { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + DisplayInfo info; + auto display = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain); + SurfaceComposerClient::getDisplayInfo(display, &info); + + // After a new buffer is queued, SurfaceFlinger is notified and will + // latch the new buffer on next vsync. Let's heuristically wait for 3 + // vsyncs. + mBufferPostDelay = int32_t(1e6 / info.fps) * 3; + } + + void TearDown() { + mComposerClient->dispose(); + } + + std::unique_ptr<InputSurface> makeSurface(int width, int height) { + return InputSurface::makeColorInputSurface(mComposerClient, width, height); + } + + void postBuffer(const sp<SurfaceControl> &layer) { + // wait for previous transactions (such as setSize) to complete + Transaction().apply(true); + ANativeWindow_Buffer buffer = {}; + EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr)); + ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost()); + // Request an empty transaction to get applied synchronously to ensure the buffer is + // latched. + Transaction().apply(true); + usleep(mBufferPostDelay); + } + + sp<SurfaceComposerClient> mComposerClient; + int32_t mBufferPostDelay; +}; + +void injectTap(int x, int y) { + char *buf1, *buf2; + asprintf(&buf1, "%d", x); + asprintf(&buf2, "%d", y); + if (fork() == 0) { + execlp("input", "input", "tap", buf1, buf2, NULL); + } +} + +TEST_F(InputSurfacesTest, can_receive_input) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + + injectTap(101, 101); + + EXPECT_TRUE(surface->consumeEvent() != nullptr); +} + +TEST_F(InputSurfacesTest, input_respects_positioning) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + + std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); + surface2->showAt(200, 200); + + injectTap(201, 201); + surface2->expectTap(1, 1); + + injectTap(101, 101); + surface->expectTap(1, 1); + + surface2->doTransaction([](auto &t, auto &sc) { + t.setPosition(sc, 100, 100); + }); + surface->doTransaction([](auto &t, auto &sc) { + t.setPosition(sc, 200, 200); + }); + + injectTap(101, 101); + surface2->expectTap(1, 1); + + injectTap(201, 201); + surface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_layering) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); + + surface->showAt(10, 10); + surface2->showAt(10, 10); + + surface->doTransaction([](auto &t, auto &sc) { + t.setLayer(sc, LAYER_BASE + 1); + }); + + injectTap(11, 11); + surface->expectTap(1, 1); + + surface2->doTransaction([](auto &t, auto &sc) { + t.setLayer(sc, LAYER_BASE + 1); + }); + + injectTap(11, 11); + surface2->expectTap(1, 1); + + surface2->doTransaction([](auto &t, auto &sc) { + t.hide(sc); + }); + + injectTap(11, 11); + surface->expectTap(1, 1); +} + +// Surface Insets are set to offset the client content and draw a border around the client surface +// (such as shadows in dialogs). Inputs sent to the client are offset such that 0,0 is the start +// of the client content. +TEST_F(InputSurfacesTest, input_respects_surface_insets) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); + bgSurface->showAt(100, 100); + + fgSurface->mInputInfo.surfaceInset = 5; + fgSurface->showAt(100, 100); + + injectTap(106, 106); + fgSurface->expectTap(1, 1); + + injectTap(101, 101); + bgSurface->expectTap(1, 1); +} + +// Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463 +TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { + std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100); + parentSurface->showAt(100, 100); + + childSurface->mInputInfo.surfaceInset = 10; + childSurface->showAt(100, 100); + + childSurface->doTransaction([&](auto &t, auto &sc) { + t.setPosition(sc, -5, -5); + t.reparent(sc, parentSurface->mSurfaceControl->getHandle()); + }); + + injectTap(106, 106); + childSurface->expectTap(1, 1); + + injectTap(101, 101); + parentSurface->expectTap(1, 1); +} + +// Ensure we ignore transparent region when getting screen bounds when positioning input frame. +TEST_F(InputSurfacesTest, input_ignores_transparent_region) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->doTransaction([](auto &t, auto &sc) { + Region transparentRegion(Rect(0, 0, 10, 10)); + t.setTransparentRegionHint(sc, transparentRegion); + }); + surface->showAt(100, 100); + injectTap(101, 101); + surface->expectTap(1, 1); +} + +// Ensure we send the input to the right surface when the surface visibility changes due to the +// first buffer being submitted. ref: b/120839715 +TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> bufferSurface = + InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); + + bgSurface->showAt(10, 10); + bufferSurface->showAt(10, 10); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); + + postBuffer(bufferSurface->mSurfaceControl); + injectTap(11, 11); + bufferSurface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> bufferSurface = + InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); + postBuffer(bufferSurface->mSurfaceControl); + + bgSurface->showAt(10, 10); + bufferSurface->showAt(10, 10); + + injectTap(11, 11); + bufferSurface->expectTap(1, 1); + + bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); }); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); + + bgSurface->showAt(10, 10); + fgSurface->showAt(10, 10); + + injectTap(11, 11); + fgSurface->expectTap(1, 1); + + fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); }); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> containerSurface = + InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); + + bgSurface->showAt(10, 10); + containerSurface->showAt(10, 10); + + injectTap(11, 11); + containerSurface->expectTap(1, 1); + + containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); }); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); +} +} +} diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp index ccd674fcb8..b60995a624 100644 --- a/libs/gui/tests/FillBuffer.cpp +++ b/libs/gui/tests/FillBuffer.cpp @@ -93,11 +93,11 @@ void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) { android_native_buffer_t* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); - uint8_t* img = NULL; + uint8_t* img = nullptr; ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img))); fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride()); diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp index a91552f7fe..a1405fcb11 100644 --- a/libs/gui/tests/GLTest.cpp +++ b/libs/gui/tests/GLTest.cpp @@ -50,7 +50,7 @@ void GLTest::SetUp() { ASSERT_EQ(EGL_SUCCESS, eglGetError()); char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS"); - if (displaySecsEnv != NULL) { + if (displaySecsEnv != nullptr) { mDisplaySecs = atoi(displaySecsEnv); if (mDisplaySecs < 0) { mDisplaySecs = 0; @@ -67,7 +67,7 @@ void GLTest::SetUp() { String8("Test Surface"), getSurfaceWidth(), getSurfaceHeight(), PIXEL_FORMAT_RGB_888, 0); - ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl != nullptr); ASSERT_TRUE(mSurfaceControl->isValid()); Transaction t; @@ -117,7 +117,7 @@ void GLTest::TearDown() { sleep(mDisplaySecs); } - if (mComposerClient != NULL) { + if (mComposerClient != nullptr) { mComposerClient->dispose(); } if (mEglContext != EGL_NO_CONTEXT) { @@ -171,7 +171,7 @@ EGLint GLTest::getSurfaceHeight() { EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, sp<ANativeWindow>& window) const { - return eglCreateWindowSurface(display, config, window.get(), NULL); + return eglCreateWindowSurface(display, config, window.get(), nullptr); } ::testing::AssertionResult GLTest::checkPixel(int x, int y, @@ -256,7 +256,7 @@ void GLTest::loadShader(GLenum shaderType, const char* pSource, GLuint shader = glCreateShader(shaderType); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); if (shader) { - glShaderSource(shader, 1, &pSource, NULL); + glShaderSource(shader, 1, &pSource, nullptr); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glCompileShader(shader); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); @@ -270,7 +270,7 @@ void GLTest::loadShader(GLenum shaderType, const char* pSource, if (infoLen) { char* buf = (char*) malloc(infoLen); if (buf) { - glGetShaderInfoLog(shader, infoLen, NULL, buf); + glGetShaderInfoLog(shader, infoLen, nullptr, buf); printf("Shader compile log:\n%s\n", buf); free(buf); FAIL(); @@ -278,7 +278,7 @@ void GLTest::loadShader(GLenum shaderType, const char* pSource, } else { char* buf = (char*) malloc(0x1000); if (buf) { - glGetShaderInfoLog(shader, 0x1000, NULL, buf); + glGetShaderInfoLog(shader, 0x1000, nullptr, buf); printf("Shader compile log:\n%s\n", buf); free(buf); FAIL(); @@ -322,7 +322,7 @@ void GLTest::createProgram(const char* pVertexSource, if (bufLength) { char* buf = (char*) malloc(bufLength); if (buf) { - glGetProgramInfoLog(program, bufLength, NULL, buf); + glGetProgramInfoLog(program, bufLength, nullptr, buf); printf("Program link log:\n%s\n", buf); free(buf); FAIL(); diff --git a/libs/gui/tests/GLTest.h b/libs/gui/tests/GLTest.h index f0d27a8a34..f290b3c68d 100644 --- a/libs/gui/tests/GLTest.h +++ b/libs/gui/tests/GLTest.h @@ -39,7 +39,7 @@ protected: mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), - mGlConfig(NULL) { + mGlConfig(nullptr) { } virtual void SetUp(); diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp index a35cf11174..aef7aed52c 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -228,9 +228,9 @@ protected: void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence, sp<GraphicBuffer> *buffer) { - ASSERT_TRUE(slot != NULL); - ASSERT_TRUE(fence != NULL); - ASSERT_TRUE(buffer != NULL); + ASSERT_TRUE(slot != nullptr); + ASSERT_TRUE(fence != nullptr); + ASSERT_TRUE(buffer != nullptr); ASSERT_NO_FATAL_FAILURE(ConnectProducer()); @@ -263,7 +263,7 @@ TEST_P(IGraphicBufferProducerTest, ConnectFirst_ReturnsError) { EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN, TEST_API, TEST_CONTROLLED_BY_APP, - /*output*/NULL)); + /*output*/nullptr)); // Invalid API returns bad value EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN, @@ -359,7 +359,7 @@ TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) { // TODO: Consider documented the above enums as unsupported or make a new enum for IGBP // Value was NULL - EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/NULL)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/nullptr)); ASSERT_OK(mConsumer->consumerDisconnect()); @@ -465,7 +465,7 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { // Fence was NULL { - sp<Fence> nullFence = NULL; + sp<Fence> nullFence = nullptr; IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder().setFence(nullFence).build(); @@ -695,10 +695,7 @@ TEST_P(IGraphicBufferProducerTest, sp<Fence> fence; sp<GraphicBuffer> buffer; - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/38137191): Implement BufferHubProducer::detachBuffer - ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence)); - } + ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence)); } TEST_P(IGraphicBufferProducerTest, @@ -735,10 +732,7 @@ TEST_P(IGraphicBufferProducerTest, ASSERT_OK(mProducer->disconnect(TEST_API)); - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/38137191): Implement BufferHubProducer::detachBuffer - ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot)); - } + ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot)); } TEST_P(IGraphicBufferProducerTest, @@ -778,18 +772,29 @@ TEST_P(IGraphicBufferProducerTest, sp<GraphicBuffer> buffer; setupDequeueRequestBuffer(&slot, &fence, &buffer); + ASSERT_TRUE(buffer != nullptr); - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/38137191): Implement BufferHubProducer::detachBuffer - ASSERT_OK(mProducer->detachBuffer(slot)); - } + ASSERT_OK(mProducer->detachBuffer(slot)); + EXPECT_OK(buffer->initCheck()); ASSERT_OK(mProducer->disconnect(TEST_API)); - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/69981968): Implement BufferHubProducer::attachBuffer - ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer)); - } + ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer)); +} + +TEST_P(IGraphicBufferProducerTest, DetachThenAttach_Succeeds) { + int slot = -1; + sp<Fence> fence; + sp<GraphicBuffer> buffer; + + setupDequeueRequestBuffer(&slot, &fence, &buffer); + ASSERT_TRUE(buffer != nullptr); + + ASSERT_OK(mProducer->detachBuffer(slot)); + EXPECT_OK(buffer->initCheck()); + + EXPECT_OK(mProducer->attachBuffer(&slot, buffer)); + EXPECT_OK(buffer->initCheck()); } #if USE_BUFFER_HUB_AS_BUFFER_QUEUE diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp index 3a25ac59ca..7d3d4aa412 100644 --- a/libs/gui/tests/MultiTextureConsumer_test.cpp +++ b/libs/gui/tests/MultiTextureConsumer_test.cpp @@ -47,7 +47,7 @@ protected: GLTest::TearDown(); } virtual EGLint const* getContextAttribs() { - return NULL; + return nullptr; } virtual EGLint const* getConfigAttribs() { static EGLint sDefaultConfigAttribs[] = { @@ -105,7 +105,7 @@ TEST_F(MultiTextureConsumerTest, EGLImageTargetWorks) { glClear(GL_COLOR_BUFFER_BIT); for (int i=0 ; i<8 ; i++) { - mSurface->lock(&buffer, NULL); + mSurface->lock(&buffer, nullptr); memset(buffer.bits, (i&7) * 0x20, buffer.stride * buffer.height * 4); mSurface->unlockAndPost(); diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index d5b2f004ed..65e09f2540 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -29,7 +29,6 @@ #include <utils/Thread.h> extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); -#define CROP_EXT_STR "EGL_ANDROID_image_crop" namespace android { @@ -39,7 +38,7 @@ protected: mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), - mEglConfig(NULL) { + mEglConfig(nullptr) { } virtual void SetUp() { @@ -82,7 +81,7 @@ protected: ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mEglSurface); - mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0); + mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mEglContext); @@ -127,7 +126,7 @@ protected: TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) { sp<IGraphicBufferProducer> ist(mSTC->getIGraphicBufferProducer()); - ASSERT_TRUE(ist != NULL); + ASSERT_TRUE(ist != nullptr); } TEST_F(SurfaceTextureClientTest, QueuesToWindowCompositorIsFalse) { @@ -155,7 +154,7 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { EXPECT_TRUE(eglInitialize(dpy, &majorVersion, &minorVersion)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); - EGLConfig myConfig = {0}; + EGLConfig myConfig = {nullptr}; EGLint numConfigs = 0; EGLint configAttribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, @@ -172,7 +171,7 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { ASSERT_EQ(EGL_SUCCESS, eglGetError()); EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, mANW.get(), - NULL); + nullptr); EXPECT_NE(EGL_NO_SURFACE, eglSurface); EXPECT_EQ(EGL_SUCCESS, eglGetError()); @@ -185,7 +184,7 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { TEST_F(SurfaceTextureClientTest, EglSwapBuffersAbandonErrorIsEglBadSurface) { - EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), NULL); + EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), nullptr); EXPECT_NE(EGL_NO_SURFACE, eglSurface); EXPECT_EQ(EGL_SUCCESS, eglGetError()); @@ -638,18 +637,6 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) } TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) { - // Query to see if the image crop extension exists - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - size_t cropExtLen = strlen(CROP_EXT_STR); - size_t extsLen = strlen(exts); - bool equal = !strcmp(CROP_EXT_STR, exts); - bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1); - bool atEnd = (cropExtLen+1) < extsLen && - !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1)); - bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); - bool hasEglAndroidImageCrop = equal || atStart || atEnd || inMiddle; - android_native_buffer_t* buf[3]; float mtx[16] = {}; android_native_rect_t crop; @@ -669,17 +656,15 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWi ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers mST->getTransformMatrix(mtx); - // If the egl image crop extension is not present, this accounts for the - // .5 texel shrink for each edge that's included in the transform matrix - // to avoid texturing outside the crop region. Otherwise the crop is not - // included in the transform matrix. - EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5, mtx[0]); + // This accounts for the .5 texel shrink for each edge that's included in + // the transform matrix to avoid texturing outside the crop region. + EXPECT_EQ(0.5f, mtx[0]); EXPECT_EQ(0.f, mtx[1]); EXPECT_EQ(0.f, mtx[2]); EXPECT_EQ(0.f, mtx[3]); EXPECT_EQ(0.f, mtx[4]); - EXPECT_EQ(hasEglAndroidImageCrop ? -1 : -0.5, mtx[5]); + EXPECT_EQ(-0.5f, mtx[5]); EXPECT_EQ(0.f, mtx[6]); EXPECT_EQ(0.f, mtx[7]); @@ -688,8 +673,8 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWi EXPECT_EQ(1.f, mtx[10]); EXPECT_EQ(0.f, mtx[11]); - EXPECT_EQ(hasEglAndroidImageCrop ? 0 : 0.0625f, mtx[12]); - EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5625f, mtx[13]); + EXPECT_EQ(0.0625f, mtx[12]); + EXPECT_EQ(0.5625f, mtx[13]); EXPECT_EQ(0.f, mtx[14]); EXPECT_EQ(1.f, mtx[15]); } @@ -753,7 +738,7 @@ protected: ASSERT_EQ(EGL_SUCCESS, eglGetError()); mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, - 0); + nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mEglContext); @@ -765,7 +750,7 @@ protected: GLConsumer::TEXTURE_EXTERNAL, true, false)); sp<Surface> stc(new Surface(producer)); mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig, - static_cast<ANativeWindow*>(stc.get()), NULL); + static_cast<ANativeWindow*>(stc.get()), nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mEglSurfaces[i]); } diff --git a/libs/gui/tests/SurfaceTextureFBO.h b/libs/gui/tests/SurfaceTextureFBO.h index 7f1ae84c48..70f988de11 100644 --- a/libs/gui/tests/SurfaceTextureFBO.h +++ b/libs/gui/tests/SurfaceTextureFBO.h @@ -34,7 +34,7 @@ protected: glGenTextures(1, &mFboTex); glBindTexture(GL_TEXTURE_2D, mFboTex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(), - getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glBindTexture(GL_TEXTURE_2D, 0); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp index 0134273a07..f34561f668 100644 --- a/libs/gui/tests/SurfaceTextureFBO_test.cpp +++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp @@ -39,12 +39,12 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { android_native_buffer_t* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with green - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255, 0, 255); @@ -63,7 +63,7 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); buf = GraphicBuffer::from(anb); diff --git a/libs/gui/tests/SurfaceTextureGLThreadToGL.h b/libs/gui/tests/SurfaceTextureGLThreadToGL.h index 2ce20eb2b1..03975b1261 100644 --- a/libs/gui/tests/SurfaceTextureGLThreadToGL.h +++ b/libs/gui/tests/SurfaceTextureGLThreadToGL.h @@ -158,7 +158,7 @@ protected: } virtual void TearDown() { - if (mProducerThread != NULL) { + if (mProducerThread != nullptr) { mProducerThread->requestExitAndWait(); } mProducerThread.clear(); @@ -167,7 +167,7 @@ protected: } void runProducerThread(const sp<ProducerThread> producerThread) { - ASSERT_TRUE(mProducerThread == NULL); + ASSERT_TRUE(mProducerThread == nullptr); mProducerThread = producerThread; producerThread->setEglObjects(mEglDisplay, mProducerEglSurface, mProducerEglContext); diff --git a/libs/gui/tests/SurfaceTextureGLToGL.h b/libs/gui/tests/SurfaceTextureGLToGL.h index 5d43a48898..3a87c12cf6 100644 --- a/libs/gui/tests/SurfaceTextureGLToGL.h +++ b/libs/gui/tests/SurfaceTextureGLToGL.h @@ -38,7 +38,7 @@ protected: void SetUpWindowAndContext() { mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, - mANW.get(), NULL); + mANW.get(), nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface); diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp index 56392867ea..e2b4f3d035 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -40,12 +40,12 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with the a checkerboard pattern - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); @@ -90,12 +90,12 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) { ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with the a checkerboard pattern - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); @@ -155,11 +155,11 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop); buf->unlock(); @@ -234,7 +234,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } @@ -248,7 +248,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * texHeight/2; int yuvTexStrideU = yuvTexStrideV; - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); // Gray out all the test pixels first, so we're more likely to @@ -457,7 +457,7 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) @@ -641,7 +641,7 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) @@ -654,7 +654,7 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 6e196bfac8..67afbd6a52 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -72,7 +72,7 @@ protected: mSurfaceControl = mComposerClient->createSurface( String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0); - ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl != nullptr); ASSERT_TRUE(mSurfaceControl->isValid()); Transaction t; @@ -81,7 +81,7 @@ protected: .apply()); mSurface = mSurfaceControl->getSurface(); - ASSERT_TRUE(mSurface != NULL); + ASSERT_TRUE(mSurface != nullptr); } virtual void TearDown() { @@ -134,8 +134,9 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { sp<IBinder> display(sf->getBuiltInDisplay( ISurfaceComposer::eDisplayIdMain)); sp<GraphicBuffer> outBuffer; - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(), - 64, 64, 0, 0x7fffffff, false)); + ASSERT_EQ(NO_ERROR, + sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); @@ -145,7 +146,7 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), GRALLOC_USAGE_PROTECTED)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3)); - ANativeWindowBuffer* buf = 0; + ANativeWindowBuffer* buf = nullptr; status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf); if (err) { @@ -165,8 +166,9 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { &buf)); ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1)); } - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(), - 64, 64, 0, 0x7fffffff, false)); + ASSERT_EQ(NO_ERROR, + sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); } TEST_F(SurfaceTest, ConcreteTypeIsSurface) { @@ -205,7 +207,7 @@ TEST_F(SurfaceTest, QueryConsumerUsage) { } TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) { - const android_dataspace TEST_DATASPACE = HAL_DATASPACE_SRGB; + const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB; sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); @@ -364,10 +366,17 @@ TEST_F(SurfaceTest, SetHdrMetadata) { 78.0, 62.0, }; + + std::vector<uint8_t> hdr10plus; + hdr10plus.push_back(0xff); + int error = native_window_set_buffers_smpte2086_metadata(window.get(), &smpte2086); ASSERT_EQ(error, NO_ERROR); error = native_window_set_buffers_cta861_3_metadata(window.get(), &cta861_3); ASSERT_EQ(error, NO_ERROR); + error = native_window_set_buffers_hdr10_plus_metadata(window.get(), hdr10plus.size(), + hdr10plus.data()); + ASSERT_EQ(error, NO_ERROR); } TEST_F(SurfaceTest, DynamicSetBufferCount) { @@ -581,9 +590,6 @@ public: Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; } status_t getDisplayStats(const sp<IBinder>& /*display*/, DisplayStatInfo* /*stats*/) override { return NO_ERROR; } - status_t getDisplayViewport(const sp<IBinder>& /*display*/, Rect* /*outViewport*/) override { - return NO_ERROR; - } int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; } status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/) override { @@ -599,15 +605,19 @@ public: } status_t setActiveColorMode(const sp<IBinder>& /*display*/, ColorMode /*colorMode*/) override { return NO_ERROR; } - status_t captureScreen(const sp<IBinder>& /*display*/, - sp<GraphicBuffer>* /*outBuffer*/, - Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, - int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/, - bool /*useIdentityTransform*/, - Rotation /*rotation*/) override { return NO_ERROR; } + status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/, + const ui::Dataspace /*reqDataspace*/, + const ui::PixelFormat /*reqPixelFormat*/, Rect /*sourceCrop*/, + uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, + bool /*useIdentityTransform*/, Rotation /*rotation*/) override { + return NO_ERROR; + } virtual status_t captureLayers(const sp<IBinder>& /*parentHandle*/, - sp<GraphicBuffer>* /*outBuffer*/, const Rect& /*sourceCrop*/, - float /*frameScale*/, bool /*childrenOnly*/) override { + sp<GraphicBuffer>* /*outBuffer*/, + const ui::Dataspace /*reqDataspace*/, + const ui::PixelFormat /*reqPixelFormat*/, + const Rect& /*sourceCrop*/, float /*frameScale*/, + bool /*childrenOnly*/) override { return NO_ERROR; } status_t clearAnimationFrameStats() override { return NO_ERROR; } @@ -625,6 +635,30 @@ public: status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override { return NO_ERROR; } + status_t getCompositionPreference( + ui::Dataspace* /*outDefaultDataspace*/, ui::PixelFormat* /*outDefaultPixelFormat*/, + ui::Dataspace* /*outWideColorGamutDataspace*/, + ui::PixelFormat* /*outWideColorGamutPixelFormat*/) const override { + return NO_ERROR; + } + status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& /*display*/, + ui::PixelFormat* /*outFormat*/, + ui::Dataspace* /*outDataspace*/, + uint8_t* /*outComponentMask*/) const override { + return NO_ERROR; + } + status_t setDisplayContentSamplingEnabled(const sp<IBinder>& /*display*/, bool /*enable*/, + uint8_t /*componentMask*/, + uint64_t /*maxFrames*/) const override { + return NO_ERROR; + } + status_t getDisplayedContentSample(const sp<IBinder>& /*display*/, uint64_t /*maxFrames*/, + uint64_t /*timestamp*/, + DisplayedFrameStats* /*outStats*/) const override { + return NO_ERROR; + } + + virtual status_t getColorManagement(bool* /*outGetColorManagement*/) const { return NO_ERROR; } protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 2f399765a0..fc676f14e9 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -42,15 +42,18 @@ cc_library { target: { android: { srcs: [ - "IInputFlinger.cpp", "InputTransport.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", + "InputApplication.cpp", + "InputWindow.cpp", + "IInputFlinger.cpp" ], shared_libs: [ "libutils", "libbinder", + "libui" ], sanitize: { diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp index 003e73dae6..139570a5fd 100644 --- a/libs/input/IInputFlinger.cpp +++ b/libs/input/IInputFlinger.cpp @@ -23,7 +23,6 @@ #include <input/IInputFlinger.h> - namespace android { class BpInputFlinger : public BpInterface<IInputFlinger> { @@ -31,23 +30,64 @@ public: explicit BpInputFlinger(const sp<IBinder>& impl) : BpInterface<IInputFlinger>(impl) { } - virtual status_t doSomething() { + virtual void setInputWindows(const Vector<InputWindowInfo>& inputInfo) { Parcel data, reply; data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - remote()->transact(BnInputFlinger::DO_SOMETHING_TRANSACTION, data, &reply); - return reply.readInt32(); + + data.writeUint32(static_cast<uint32_t>(inputInfo.size())); + for (const auto& info : inputInfo) { + info.write(data); + } + remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply, + IBinder::FLAG_ONEWAY); + } + + virtual void registerInputChannel(const sp<InputChannel>& channel) { + Parcel data, reply; + data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); + channel->write(data); + remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); + } + + virtual void unregisterInputChannel(const sp<InputChannel>& channel) { + Parcel data, reply; + data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); + channel->write(data); + remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); } }; IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger"); - status_t BnInputFlinger::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { - case DO_SOMETHING_TRANSACTION: { + case SET_INPUT_WINDOWS_TRANSACTION: { + CHECK_INTERFACE(IInputFlinger, data, reply); + size_t count = data.readUint32(); + if (count > data.dataSize()) { + return BAD_VALUE; + } + Vector<InputWindowInfo> handles; + handles.setCapacity(count); + for (size_t i = 0; i < count; i++) { + handles.add(InputWindowInfo(data)); + } + setInputWindows(handles); + break; + } + case REGISTER_INPUT_CHANNEL_TRANSACTION: { + CHECK_INTERFACE(IInputFlinger, data, reply); + sp<InputChannel> channel = new InputChannel(); + channel->read(data); + registerInputChannel(channel); + break; + } + case UNREGISTER_INPUT_CHANNEL_TRANSACTION: { CHECK_INTERFACE(IInputFlinger, data, reply); - reply->writeInt32(0); + sp<InputChannel> channel = new InputChannel(); + channel->read(data); + unregisterInputChannel(channel); break; } default: diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index a6246636a3..a558970b58 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -31,14 +31,16 @@ namespace android { // --- InputEvent --- -void InputEvent::initialize(int32_t deviceId, int32_t source) { +void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) { mDeviceId = deviceId; mSource = source; + mDisplayId = displayId; } void InputEvent::initialize(const InputEvent& from) { mDeviceId = from.mDeviceId; mSource = from.mSource; + mDisplayId = from.mDisplayId; } // --- KeyEvent --- @@ -54,6 +56,7 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { void KeyEvent::initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -62,7 +65,7 @@ void KeyEvent::initialize( int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { - InputEvent::initialize(deviceId, source); + InputEvent::initialize(deviceId, source, displayId); mAction = action; mFlags = flags; mKeyCode = keyCode; @@ -128,15 +131,24 @@ static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) } } -void PointerCoords::scale(float scaleFactor) { +void PointerCoords::scale(float globalScaleFactor, float windowXScale, float windowYScale) { // No need to scale pressure or size since they are normalized. // No need to scale orientation since it is meaningless to do so. - scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); + + // If there is a global scale factor, it is included in the windowX/YScale + // so we don't need to apply it twice to the X/Y axes. + // However we don't want to apply any windowXYScale not included in the global scale + // to the TOUCH_MAJOR/MINOR coordinates. + scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, windowXScale); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, windowYScale); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, globalScaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, globalScaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, globalScaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, globalScaleFactor); +} + +void PointerCoords::scale(float globalScaleFactor) { + scale(globalScaleFactor, globalScaleFactor, globalScaleFactor); } void PointerCoords::applyOffset(float xOffset, float yOffset) { @@ -215,6 +227,7 @@ void PointerProperties::copyFrom(const PointerProperties& other) { void MotionEvent::initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, @@ -230,7 +243,7 @@ void MotionEvent::initialize( size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { - InputEvent::initialize(deviceId, source); + InputEvent::initialize(deviceId, source, displayId); mAction = action; mActionButton = actionButton; mFlags = flags; @@ -250,7 +263,7 @@ void MotionEvent::initialize( } void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { - InputEvent::initialize(other->mDeviceId, other->mSource); + InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId); mAction = other->mAction; mActionButton = other->mActionButton; mFlags = other->mFlags; @@ -341,15 +354,15 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { mYOffset += yOffset; } -void MotionEvent::scale(float scaleFactor) { - mXOffset *= scaleFactor; - mYOffset *= scaleFactor; - mXPrecision *= scaleFactor; - mYPrecision *= scaleFactor; +void MotionEvent::scale(float globalScaleFactor) { + mXOffset *= globalScaleFactor; + mYOffset *= globalScaleFactor; + mXPrecision *= globalScaleFactor; + mYPrecision *= globalScaleFactor; size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { - mSamplePointerCoords.editItemAt(i).scale(scaleFactor); + mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor); } } @@ -431,6 +444,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mDeviceId = parcel->readInt32(); mSource = parcel->readInt32(); + mDisplayId = parcel->readInt32(); mAction = parcel->readInt32(); mActionButton = parcel->readInt32(); mFlags = parcel->readInt32(); @@ -480,6 +494,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mDeviceId); parcel->writeInt32(mSource); + parcel->writeInt32(mDisplayId); parcel->writeInt32(mAction); parcel->writeInt32(mActionButton); parcel->writeInt32(mFlags); diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp new file mode 100644 index 0000000000..7936f50d54 --- /dev/null +++ b/libs/input/InputApplication.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputApplication" + +#include <input/InputApplication.h> + +#include <android/log.h> + +namespace android { + +// --- InputApplicationHandle --- + +InputApplicationHandle::InputApplicationHandle() : + mInfo(nullptr) { +} + +InputApplicationHandle::~InputApplicationHandle() { + delete mInfo; +} + +void InputApplicationHandle::releaseInfo() { + if (mInfo) { + delete mInfo; + mInfo = nullptr; + } +} + +InputApplicationInfo InputApplicationInfo::read(const Parcel& from) { + InputApplicationInfo ret; + ret.token = from.readStrongBinder(); + ret.name = from.readString8().c_str(); + ret.dispatchingTimeout = from.readInt64(); + + return ret; +} + +status_t InputApplicationInfo::write(Parcel& output) const { + output.writeStrongBinder(token); + output.writeString8(String8(name.c_str())); + output.writeInt64(dispatchingTimeout); + + return OK; +} + +} // namespace android diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 4287abeb7d..778c4539fa 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -20,9 +20,12 @@ #include <unistd.h> #include <ctype.h> +#include <android-base/stringprintf.h> #include <input/InputDevice.h> #include <input/InputEventLabels.h> +using android::base::StringPrintf; + namespace android { static const char* CONFIGURATION_FILE_DIR[] = { @@ -41,8 +44,8 @@ static bool isValidNameChar(char ch) { return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); } -static void appendInputDeviceConfigurationFileRelativePath(String8& path, - const String8& name, InputDeviceConfigurationFileType type) { +static void appendInputDeviceConfigurationFileRelativePath(std::string& path, + const std::string& name, InputDeviceConfigurationFileType type) { path.append(CONFIGURATION_FILE_DIR[type]); for (size_t i = 0; i < name.length(); i++) { char ch = name[i]; @@ -54,28 +57,28 @@ static void appendInputDeviceConfigurationFileRelativePath(String8& path, path.append(CONFIGURATION_FILE_EXTENSION[type]); } -String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( +std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type) { if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { if (deviceIdentifier.version != 0) { // Try vendor product version. - String8 versionPath(getInputDeviceConfigurationFilePathByName( - String8::format("Vendor_%04x_Product_%04x_Version_%04x", + std::string versionPath = getInputDeviceConfigurationFilePathByName( + StringPrintf("Vendor_%04x_Product_%04x_Version_%04x", deviceIdentifier.vendor, deviceIdentifier.product, deviceIdentifier.version), - type)); - if (!versionPath.isEmpty()) { + type); + if (!versionPath.empty()) { return versionPath; } } // Try vendor product. - String8 productPath(getInputDeviceConfigurationFilePathByName( - String8::format("Vendor_%04x_Product_%04x", + std::string productPath = getInputDeviceConfigurationFilePathByName( + StringPrintf("Vendor_%04x_Product_%04x", deviceIdentifier.vendor, deviceIdentifier.product), - type)); - if (!productPath.isEmpty()) { + type); + if (!productPath.empty()) { return productPath; } } @@ -84,22 +87,25 @@ String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); } -String8 getInputDeviceConfigurationFilePathByName( - const String8& name, InputDeviceConfigurationFileType type) { +std::string getInputDeviceConfigurationFilePathByName( + const std::string& name, InputDeviceConfigurationFileType type) { // Search system repository. - String8 path; + std::string path; // Treblized input device config files will be located /odm/usr or /vendor/usr. const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")}; for (size_t i = 0; i < size(rootsForPartition); i++) { - path.setTo(rootsForPartition[i]); - path.append("/usr/"); + if (rootsForPartition[i] == nullptr) { + continue; + } + path = rootsForPartition[i]; + path += "/usr/"; appendInputDeviceConfigurationFileRelativePath(path, name, type); #if DEBUG_PROBE ALOGD("Probing for system provided input device configuration file: path='%s'", - path.string()); + path.c_str()); #endif - if (!access(path.string(), R_OK)) { + if (!access(path.c_str(), R_OK)) { #if DEBUG_PROBE ALOGD("Found"); #endif @@ -109,13 +115,17 @@ String8 getInputDeviceConfigurationFilePathByName( // Search user repository. // TODO Should only look here if not in safe mode. - path.setTo(getenv("ANDROID_DATA")); - path.append("/system/devices/"); + path = ""; + char *androidData = getenv("ANDROID_DATA"); + if (androidData != nullptr) { + path += androidData; + } + path += "/system/devices/"; appendInputDeviceConfigurationFileRelativePath(path, name, type); #if DEBUG_PROBE - ALOGD("Probing for system user input device configuration file: path='%s'", path.string()); + ALOGD("Probing for system user input device configuration file: path='%s'", path.c_str()); #endif - if (!access(path.string(), R_OK)) { + if (!access(path.c_str(), R_OK)) { #if DEBUG_PROBE ALOGD("Found"); #endif @@ -125,16 +135,16 @@ String8 getInputDeviceConfigurationFilePathByName( // Not found. #if DEBUG_PROBE ALOGD("Probe failed to find input device configuration file: name='%s', type=%d", - name.string(), type); + name.c_str(), type); #endif - return String8(); + return ""; } // --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() { - initialize(-1, 0, -1, InputDeviceIdentifier(), String8(), false, false); + initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false); } InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : @@ -150,7 +160,7 @@ InputDeviceInfo::~InputDeviceInfo() { } void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal, + const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal, bool hasMic) { mId = id; mGeneration = generation; @@ -175,7 +185,7 @@ const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( return ⦥ } } - return NULL; + return nullptr; } void InputDeviceInfo::addSource(uint32_t source) { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index aa0bf17ca3..f33b210c4c 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -30,6 +30,7 @@ #include <cutils/properties.h> #include <log/log.h> +#include <binder/Parcel.h> #include <input/InputTransport.h> namespace android { @@ -96,19 +97,117 @@ size_t InputMessage::size() const { return sizeof(Header); } +/** + * There could be non-zero bytes in-between InputMessage fields. Force-initialize the entire + * memory to zero, then only copy the valid bytes on a per-field basis. + */ +void InputMessage::getSanitizedCopy(InputMessage* msg) const { + memset(msg, 0, sizeof(*msg)); + + // Write the header + msg->header.type = header.type; + + // Write the body + switch(header.type) { + case InputMessage::TYPE_KEY: { + // uint32_t seq + msg->body.key.seq = body.key.seq; + // nsecs_t eventTime + msg->body.key.eventTime = body.key.eventTime; + // int32_t deviceId + msg->body.key.deviceId = body.key.deviceId; + // int32_t source + msg->body.key.source = body.key.source; + // int32_t displayId + msg->body.key.displayId = body.key.displayId; + // int32_t action + msg->body.key.action = body.key.action; + // int32_t flags + msg->body.key.flags = body.key.flags; + // int32_t keyCode + msg->body.key.keyCode = body.key.keyCode; + // int32_t scanCode + msg->body.key.scanCode = body.key.scanCode; + // int32_t metaState + msg->body.key.metaState = body.key.metaState; + // int32_t repeatCount + msg->body.key.repeatCount = body.key.repeatCount; + // nsecs_t downTime + msg->body.key.downTime = body.key.downTime; + break; + } + case InputMessage::TYPE_MOTION: { + // uint32_t seq + msg->body.motion.seq = body.motion.seq; + // nsecs_t eventTime + msg->body.motion.eventTime = body.motion.eventTime; + // int32_t deviceId + msg->body.motion.deviceId = body.motion.deviceId; + // int32_t source + msg->body.motion.source = body.motion.source; + // int32_t displayId + msg->body.motion.displayId = body.motion.displayId; + // int32_t action + msg->body.motion.action = body.motion.action; + // int32_t actionButton + msg->body.motion.actionButton = body.motion.actionButton; + // int32_t flags + msg->body.motion.flags = body.motion.flags; + // int32_t metaState + msg->body.motion.metaState = body.motion.metaState; + // int32_t buttonState + msg->body.motion.buttonState = body.motion.buttonState; + // int32_t edgeFlags + msg->body.motion.edgeFlags = body.motion.edgeFlags; + // nsecs_t downTime + msg->body.motion.downTime = body.motion.downTime; + // float xOffset + msg->body.motion.xOffset = body.motion.xOffset; + // float yOffset + msg->body.motion.yOffset = body.motion.yOffset; + // float xPrecision + msg->body.motion.xPrecision = body.motion.xPrecision; + // float yPrecision + msg->body.motion.yPrecision = body.motion.yPrecision; + // uint32_t pointerCount + msg->body.motion.pointerCount = body.motion.pointerCount; + //struct Pointer pointers[MAX_POINTERS] + for (size_t i = 0; i < body.motion.pointerCount; i++) { + // PointerProperties properties + msg->body.motion.pointers[i].properties.id = body.motion.pointers[i].properties.id; + msg->body.motion.pointers[i].properties.toolType = + body.motion.pointers[i].properties.toolType, + // PointerCoords coords + msg->body.motion.pointers[i].coords.bits = body.motion.pointers[i].coords.bits; + const uint32_t count = BitSet64::count(body.motion.pointers[i].coords.bits); + memcpy(&msg->body.motion.pointers[i].coords.values[0], + &body.motion.pointers[i].coords.values[0], + count * (sizeof(body.motion.pointers[i].coords.values[0]))); + } + break; + } + case InputMessage::TYPE_FINISHED: { + msg->body.finished.seq = body.finished.seq; + msg->body.finished.handled = body.finished.handled; + break; + } + default: { + LOG_FATAL("Unexpected message type %i", header.type); + break; + } + } +} // --- InputChannel --- InputChannel::InputChannel(const std::string& name, int fd) : - mName(name), mFd(fd) { + mName(name) { #if DEBUG_CHANNEL_LIFECYCLE ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), fd); #endif - int result = fcntl(mFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " - "non-blocking. errno=%d", mName.c_str(), errno); + setFd(fd); } InputChannel::~InputChannel() { @@ -120,6 +219,18 @@ InputChannel::~InputChannel() { ::close(mFd); } +void InputChannel::setFd(int fd) { + if (mFd > 0) { + ::close(mFd); + } + mFd = fd; + if (mFd > 0) { + int result = fcntl(mFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " + "non-blocking. errno=%d", mName.c_str(), errno); + } +} + status_t InputChannel::openInputChannelPair(const std::string& name, sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { int sockets[2]; @@ -149,10 +260,12 @@ status_t InputChannel::openInputChannelPair(const std::string& name, } status_t InputChannel::sendMessage(const InputMessage* msg) { - size_t msgLength = msg->size(); + const size_t msgLength = msg->size(); + InputMessage cleanMsg; + msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { - nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); + nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { @@ -226,10 +339,51 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { sp<InputChannel> InputChannel::dup() const { int fd = ::dup(getFd()); - return fd >= 0 ? new InputChannel(getName(), fd) : NULL; + return fd >= 0 ? new InputChannel(getName(), fd) : nullptr; } +status_t InputChannel::write(Parcel& out) const { + status_t s = out.writeString8(String8(getName().c_str())); + + if (s != OK) { + return s; + } + s = out.writeStrongBinder(mToken); + if (s != OK) { + return s; + } + + s = out.writeDupFileDescriptor(getFd()); + + return s; +} + +status_t InputChannel::read(const Parcel& from) { + mName = from.readString8(); + mToken = from.readStrongBinder(); + + int rawFd = from.readFileDescriptor(); + setFd(::dup(rawFd)); + + if (mFd < 0) { + return BAD_VALUE; + } + + return OK; +} + +sp<IBinder> InputChannel::getToken() const { + return mToken; +} + +void InputChannel::setToken(const sp<IBinder>& token) { + if (mToken != nullptr) { + ALOGE("Assigning InputChannel (%s) a second handle?", mName.c_str()); + } + mToken = token; +} + // --- InputPublisher --- InputPublisher::InputPublisher(const sp<InputChannel>& channel) : @@ -243,6 +397,7 @@ status_t InputPublisher::publishKeyEvent( uint32_t seq, int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -270,6 +425,7 @@ status_t InputPublisher::publishKeyEvent( msg.body.key.seq = seq; msg.body.key.deviceId = deviceId; msg.body.key.source = source; + msg.body.key.displayId = displayId; msg.body.key.action = action; msg.body.key.flags = flags; msg.body.key.keyCode = keyCode; @@ -303,13 +459,15 @@ status_t InputPublisher::publishMotionEvent( const PointerCoords* pointerCoords) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " + "displayId=%" PRId32 ", " "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " "metaState=0x%x, buttonState=0x%x, xOffset=%f, yOffset=%f, " "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " "pointerCount=%" PRIu32, mChannel->getName().c_str(), seq, - deviceId, source, action, actionButton, flags, edgeFlags, metaState, buttonState, - xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); + deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, + buttonState, xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, + pointerCount); #endif if (!seq) { @@ -384,7 +542,7 @@ InputConsumer::~InputConsumer() { bool InputConsumer::isTouchResamplingEnabled() { char value[PROPERTY_VALUE_MAX]; - int length = property_get("ro.input.noresample", value, NULL); + int length = property_get("ro.input.noresample", value, nullptr); if (length > 0) { if (!strcmp("1", value)) { return false; @@ -398,16 +556,14 @@ bool InputConsumer::isTouchResamplingEnabled() { } status_t InputConsumer::consume(InputEventFactoryInterface* factory, - bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, - int32_t* displayId) { + bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, mChannel->getName().c_str(), consumeBatches ? "true" : "false", frameTime); #endif *outSeq = 0; - *outEvent = NULL; - *displayId = -1; // Invalid display. + *outEvent = nullptr; // Fetch the next input message. // Loop until an event can be returned or no additional events are received. @@ -422,7 +578,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, if (result) { // Consume the next batched event unless batches are being held for later. if (consumeBatches || result != WOULD_BLOCK) { - result = consumeBatch(factory, frameTime, outSeq, outEvent, displayId); + result = consumeBatch(factory, frameTime, outSeq, outEvent); if (*outEvent) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", @@ -466,7 +622,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, // the previous batch right now and defer the new message until later. mMsgDeferred = true; status_t result = consumeSamples(factory, - batch, batch.samples.size(), outSeq, outEvent, displayId); + batch, batch.samples.size(), outSeq, outEvent); mBatches.removeAt(batchIndex); if (result) { return result; @@ -500,7 +656,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, initializeMotionEvent(motionEvent, &mMsg); *outSeq = mMsg.body.motion.seq; *outEvent = motionEvent; - *displayId = mMsg.body.motion.displayId; + #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", mChannel->getName().c_str(), *outSeq); @@ -518,14 +674,13 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, } status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) { + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { status_t result; for (size_t i = mBatches.size(); i > 0; ) { i--; Batch& batch = mBatches.editItemAt(i); if (frameTime < 0) { - result = consumeSamples(factory, batch, batch.samples.size(), - outSeq, outEvent, displayId); + result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); mBatches.removeAt(i); return result; } @@ -539,11 +694,11 @@ status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, continue; } - result = consumeSamples(factory, batch, split + 1, outSeq, outEvent, displayId); + result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); const InputMessage* next; if (batch.samples.isEmpty()) { mBatches.removeAt(i); - next = NULL; + next = nullptr; } else { next = &batch.samples.itemAt(0); } @@ -557,7 +712,7 @@ status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, } status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, - Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) { + Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) { MotionEvent* motionEvent = factory->createMotionEvent(); if (! motionEvent) return NO_MEMORY; @@ -572,7 +727,6 @@ status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, mSeqChains.push(seqChain); addSample(motionEvent, &msg); } else { - *displayId = msg.body.motion.displayId; initializeMotionEvent(motionEvent, &msg); } chain = msg.body.motion.seq; @@ -928,6 +1082,7 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) event->initialize( msg->body.key.deviceId, msg->body.key.source, + msg->body.key.displayId, msg->body.key.action, msg->body.key.flags, msg->body.key.keyCode, @@ -950,6 +1105,7 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage event->initialize( msg->body.motion.deviceId, msg->body.motion.source, + msg->body.motion.displayId, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp new file mode 100644 index 0000000000..aa1371fdc9 --- /dev/null +++ b/libs/input/InputWindow.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputWindow" +#define LOG_NDEBUG 0 + +#include <binder/Parcel.h> +#include <input/InputWindow.h> +#include <input/InputTransport.h> + +#include <log/log.h> + +#include <ui/Rect.h> +#include <ui/Region.h> + +namespace android { + +// --- InputWindowInfo --- +void InputWindowInfo::addTouchableRegion(const Rect& region) { + touchableRegion.orSelf(region); +} + +bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const { + return touchableRegion.contains(x,y); +} + +bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { + return x >= frameLeft && x < frameRight + && y >= frameTop && y < frameBottom; +} + +bool InputWindowInfo::isTrustedOverlay() const { + return layoutParamsType == TYPE_INPUT_METHOD + || layoutParamsType == TYPE_INPUT_METHOD_DIALOG + || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY + || layoutParamsType == TYPE_STATUS_BAR + || layoutParamsType == TYPE_NAVIGATION_BAR + || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL + || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY + || layoutParamsType == TYPE_DOCK_DIVIDER + || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY + || layoutParamsType == TYPE_INPUT_CONSUMER; +} + +bool InputWindowInfo::supportsSplitTouch() const { + return layoutParamsFlags & FLAG_SPLIT_TOUCH; +} + +bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { + return frameLeft < other->frameRight && frameRight > other->frameLeft + && frameTop < other->frameBottom && frameBottom > other->frameTop; +} + +status_t InputWindowInfo::write(Parcel& output) const { + if (token == nullptr) { + output.writeInt32(0); + return OK; + } + output.writeInt32(1); + status_t s = output.writeStrongBinder(token); + if (s != OK) return s; + + output.writeString8(String8(name.c_str())); + output.writeInt32(layoutParamsFlags); + output.writeInt32(layoutParamsType); + output.writeInt64(dispatchingTimeout); + output.writeInt32(frameLeft); + output.writeInt32(frameTop); + output.writeInt32(frameRight); + output.writeInt32(frameBottom); + output.writeInt32(surfaceInset); + output.writeFloat(globalScaleFactor); + output.writeFloat(windowXScale); + output.writeFloat(windowYScale); + output.writeBool(visible); + output.writeBool(canReceiveKeys); + output.writeBool(hasFocus); + output.writeBool(hasWallpaper); + output.writeBool(paused); + output.writeInt32(layer); + output.writeInt32(ownerPid); + output.writeInt32(ownerUid); + output.writeInt32(inputFeatures); + output.writeInt32(displayId); + applicationInfo.write(output); + output.write(touchableRegion); + + return OK; +} + +InputWindowInfo InputWindowInfo::read(const Parcel& from) { + InputWindowInfo ret; + + if (from.readInt32() == 0) { + return ret; + } + + sp<IBinder> token = from.readStrongBinder(); + if (token == nullptr) { + return ret; + } + + ret.token = token; + ret.name = from.readString8().c_str(); + ret.layoutParamsFlags = from.readInt32(); + ret.layoutParamsType = from.readInt32(); + ret.dispatchingTimeout = from.readInt64(); + ret.frameLeft = from.readInt32(); + ret.frameTop = from.readInt32(); + ret.frameRight = from.readInt32(); + ret.frameBottom = from.readInt32(); + ret.surfaceInset = from.readInt32(); + ret.globalScaleFactor = from.readFloat(); + ret.windowXScale = from.readFloat(); + ret.windowYScale = from.readFloat(); + ret.visible = from.readBool(); + ret.canReceiveKeys = from.readBool(); + ret.hasFocus = from.readBool(); + ret.hasWallpaper = from.readBool(); + ret.paused = from.readBool(); + ret.layer = from.readInt32(); + ret.ownerPid = from.readInt32(); + ret.ownerUid = from.readInt32(); + ret.inputFeatures = from.readInt32(); + ret.displayId = from.readInt32(); + ret.applicationInfo = InputApplicationInfo::read(from); + from.read(ret.touchableRegion); + + return ret; +} + +InputWindowInfo::InputWindowInfo(const Parcel& from) { + *this = read(from); +} + +// --- InputWindowHandle --- + +InputWindowHandle::InputWindowHandle() { +} + +InputWindowHandle::~InputWindowHandle() { +} + +void InputWindowHandle::releaseChannel() { + mInfo.token.clear(); +} + +sp<IBinder> InputWindowHandle::getToken() const { + return mInfo.token; +} + +void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) { + mInfo = handle->mInfo; +} + +} // namespace android diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index cba1111606..e189d20e28 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -106,14 +106,14 @@ KeyCharacterMap::~KeyCharacterMap() { } } -status_t KeyCharacterMap::load(const String8& filename, +status_t KeyCharacterMap::load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap) { outMap->clear(); Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); + status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { - ALOGE("Error %d opening key character map file %s.", status, filename.string()); + ALOGE("Error %d opening key character map file %s.", status, filename.c_str()); } else { status = load(tokenizer, format, outMap); delete tokenizer; @@ -121,12 +121,12 @@ status_t KeyCharacterMap::load(const String8& filename, return status; } -status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents, +status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents, Format format, sp<KeyCharacterMap>* outMap) { outMap->clear(); Tokenizer* tokenizer; - status_t status = Tokenizer::fromContents(filename, contents, &tokenizer); + status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); if (status) { ALOGE("Error %d opening key character map.", status); } else { @@ -164,10 +164,10 @@ status_t KeyCharacterMap::load(Tokenizer* tokenizer, sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base, const sp<KeyCharacterMap>& overlay) { - if (overlay == NULL) { + if (overlay == nullptr) { return base; } - if (base == NULL) { + if (base == nullptr) { return overlay; } @@ -468,7 +468,7 @@ bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMeta // Try to find the most general behavior that maps to this character. // For example, the base key behavior will usually be last in the list. - const Behavior* found = NULL; + const Behavior* found = nullptr; for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { if (behavior->character == ch) { found = behavior; @@ -487,7 +487,7 @@ void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { outEvents.push(); KeyEvent& event = outEvents.editTop(); - event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, + event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState, 0, time, time); } @@ -605,11 +605,11 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { map->mType = parcel->readInt32(); size_t numKeys = parcel->readInt32(); if (parcel->errorCheck()) { - return NULL; + return nullptr; } if (numKeys > MAX_KEYS) { ALOGE("Too many keys in KeyCharacterMap (%zu > %d)", numKeys, MAX_KEYS); - return NULL; + return nullptr; } for (size_t i = 0; i < numKeys; i++) { @@ -617,7 +617,7 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { char16_t label = parcel->readInt32(); char16_t number = parcel->readInt32(); if (parcel->errorCheck()) { - return NULL; + return nullptr; } Key* key = new Key(); @@ -625,14 +625,14 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { key->number = number; map->mKeys.add(keyCode, key); - Behavior* lastBehavior = NULL; + Behavior* lastBehavior = nullptr; while (parcel->readInt32()) { int32_t metaState = parcel->readInt32(); char16_t character = parcel->readInt32(); int32_t fallbackKeyCode = parcel->readInt32(); int32_t replacementKeyCode = parcel->readInt32(); if (parcel->errorCheck()) { - return NULL; + return nullptr; } Behavior* behavior = new Behavior(); @@ -649,7 +649,7 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { } if (parcel->errorCheck()) { - return NULL; + return nullptr; } } return map; @@ -666,7 +666,7 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { parcel->writeInt32(keyCode); parcel->writeInt32(key->label); parcel->writeInt32(key->number); - for (const Behavior* behavior = key->firstBehavior; behavior != NULL; + for (const Behavior* behavior = key->firstBehavior; behavior != nullptr; behavior = behavior->next) { parcel->writeInt32(1); parcel->writeInt32(behavior->metaState); @@ -683,12 +683,12 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { // --- KeyCharacterMap::Key --- KeyCharacterMap::Key::Key() : - label(0), number(0), firstBehavior(NULL) { + label(0), number(0), firstBehavior(nullptr) { } KeyCharacterMap::Key::Key(const Key& other) : label(other.label), number(other.number), - firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) { + firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : nullptr) { } KeyCharacterMap::Key::~Key() { @@ -704,11 +704,11 @@ KeyCharacterMap::Key::~Key() { // --- KeyCharacterMap::Behavior --- KeyCharacterMap::Behavior::Behavior() : - next(NULL), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) { + next(nullptr), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) { } KeyCharacterMap::Behavior::Behavior(const Behavior& other) : - next(other.next ? new Behavior(*other.next) : NULL), + next(other.next ? new Behavior(*other.next) : nullptr), metaState(other.metaState), character(other.character), fallbackKeyCode(other.fallbackKeyCode), replacementKeyCode(other.replacementKeyCode) { @@ -944,7 +944,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { properties.add(Property(PROPERTY_NUMBER)); } else { int32_t metaState; - status_t status = parseModifier(token, &metaState); + status_t status = parseModifier(token.string(), &metaState); if (status) { ALOGE("%s: Expected a property name or modifier, got '%s'.", mTokenizer->getLocation().string(), token.string()); @@ -1137,7 +1137,7 @@ status_t KeyCharacterMap::Parser::finishKey(Key* key) { return NO_ERROR; } -status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) { +status_t KeyCharacterMap::Parser::parseModifier(const std::string& token, int32_t* outMetaState) { if (token == "base") { *outMetaState = 0; return NO_ERROR; @@ -1145,7 +1145,7 @@ status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* o int32_t combinedMeta = 0; - const char* str = token.string(); + const char* str = token.c_str(); const char* start = str; for (const char* cur = str; ; cur++) { char ch = *cur; @@ -1164,7 +1164,7 @@ status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* o } if (combinedMeta & metaState) { ALOGE("%s: Duplicate modifier combination '%s'.", - mTokenizer->getLocation().string(), token.string()); + mTokenizer->getLocation().string(), token.c_str()); return BAD_VALUE; } diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 2b2f13e4c7..88cb0dbdb4 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -49,13 +49,13 @@ KeyLayoutMap::KeyLayoutMap() { KeyLayoutMap::~KeyLayoutMap() { } -status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) { +status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) { outMap->clear(); Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); + status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { - ALOGE("Error %d opening key layout map file %s.", status, filename.string()); + ALOGE("Error %d opening key layout map file %s.", status, filename.c_str()); } else { sp<KeyLayoutMap> map = new KeyLayoutMap(); if (!map.get()) { @@ -117,7 +117,7 @@ const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCod return &mKeysByScanCode.valueAt(index); } } - return NULL; + return nullptr; } status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const { diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index 11842ee7ff..0c22bfefed 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -45,22 +45,22 @@ status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, String8 keyLayoutName; if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"), keyLayoutName)) { - status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName); + status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " "it was not found.", - deviceIdenfifier.name.string(), keyLayoutName.string()); + deviceIdenfifier.name.c_str(), keyLayoutName.string()); } } String8 keyCharacterMapName; if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), keyCharacterMapName)) { - status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName); + status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard character " "map '%s' but it was not found.", - deviceIdenfifier.name.string(), keyLayoutName.string()); + deviceIdenfifier.name.c_str(), keyLayoutName.string()); } } @@ -70,30 +70,30 @@ status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, } // Try searching by device identifier. - if (probeKeyMap(deviceIdenfifier, String8::empty())) { + if (probeKeyMap(deviceIdenfifier, "")) { return OK; } // Fall back on the Generic key map. // TODO Apply some additional heuristics here to figure out what kind of // generic key map to use (US English, etc.) for typical external keyboards. - if (probeKeyMap(deviceIdenfifier, String8("Generic"))) { + if (probeKeyMap(deviceIdenfifier, "Generic")) { return OK; } // Try the Virtual key map as a last resort. - if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) { + if (probeKeyMap(deviceIdenfifier, "Virtual")) { return OK; } // Give up! ALOGE("Could not determine key map for device '%s' and no default key maps were found!", - deviceIdenfifier.name.string()); + deviceIdenfifier.name.c_str()); return NAME_NOT_FOUND; } bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, - const String8& keyMapName) { + const std::string& keyMapName) { if (!haveKeyLayout()) { loadKeyLayout(deviceIdentifier, keyMapName); } @@ -104,10 +104,10 @@ bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, } status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, - const String8& name) { - String8 path(getPath(deviceIdentifier, name, + const std::string& name) { + std::string path(getPath(deviceIdentifier, name, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); - if (path.isEmpty()) { + if (path.empty()) { return NAME_NOT_FOUND; } @@ -116,15 +116,15 @@ status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, return status; } - keyLayoutFile.setTo(path); + keyLayoutFile = path; return OK; } status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, - const String8& name) { - String8 path(getPath(deviceIdentifier, name, - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); - if (path.isEmpty()) { + const std::string& name) { + std::string path = getPath(deviceIdentifier, name, + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP); + if (path.empty()) { return NAME_NOT_FOUND; } @@ -134,13 +134,13 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi return status; } - keyCharacterMapFile.setTo(path); + keyCharacterMapFile = path; return OK; } -String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, - const String8& name, InputDeviceConfigurationFileType type) { - return name.isEmpty() +std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, + const std::string& name, InputDeviceConfigurationFileType type) { + return name.empty() ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) : getInputDeviceConfigurationFilePathByName(name, type); } @@ -174,7 +174,7 @@ bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, } } - return strstr(deviceIdentifier.name.string(), "-keypad"); + return strstr(deviceIdentifier.name.c_str(), "-keypad"); } static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) { diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index f834c559a4..42d774e73c 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -117,7 +117,7 @@ VelocityTracker::VelocityTracker(const char* strategy) : // Allow the default strategy to be overridden using a system property for debugging. if (!strategy) { - int length = property_get("debug.velocitytracker.strategy", value, NULL); + int length = property_get("persist.input.velocitytracker.strategy", value, nullptr); if (length > 0) { strategy = value; } else { @@ -141,7 +141,7 @@ VelocityTracker::~VelocityTracker() { bool VelocityTracker::configureStrategy(const char* strategy) { mStrategy = createStrategy(strategy); - return mStrategy != NULL; + return mStrategy != nullptr; } VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { @@ -206,7 +206,7 @@ VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { // time to adjust to changes in direction. return new LegacyVelocityTrackerStrategy(); } - return NULL; + return nullptr; } void VelocityTracker::clear() { diff --git a/libs/input/VirtualKeyMap.cpp b/libs/input/VirtualKeyMap.cpp index 28ea7177cf..3ec53bf5a0 100644 --- a/libs/input/VirtualKeyMap.cpp +++ b/libs/input/VirtualKeyMap.cpp @@ -46,13 +46,13 @@ VirtualKeyMap::VirtualKeyMap() { VirtualKeyMap::~VirtualKeyMap() { } -status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) { - *outMap = NULL; +status_t VirtualKeyMap::load(const std::string& filename, VirtualKeyMap** outMap) { + *outMap = nullptr; Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); + status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { - ALOGE("Error %d opening virtual key map file %s.", status, filename.string()); + ALOGE("Error %d opening virtual key map file %s.", status, filename.c_str()); } else { VirtualKeyMap* map = new VirtualKeyMap(); if (!map) { diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index f06119f3f7..fdd945e90e 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -6,6 +6,7 @@ cc_test { "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", "VelocityTracker_test.cpp", + "InputWindow_test.cpp" ], cflags: [ "-Wall", @@ -34,4 +35,12 @@ cc_library_static { "-Wall", "-Werror", ], + shared_libs: [ + "libinput", + "libcutils", + "libutils", + "libbinder", + "libui", + "libbase", + ] } diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index fd3b7c8ee4..99f83ba7db 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -22,6 +22,9 @@ namespace android { +// Default display id. +static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; + class BaseTest : public testing::Test { protected: virtual void SetUp() { } @@ -178,13 +181,14 @@ TEST_F(KeyEventTest, Properties) { // Initialize and get properties. const nsecs_t ARBITRARY_DOWN_TIME = 1; const nsecs_t ARBITRARY_EVENT_TIME = 2; - event.initialize(2, AINPUT_SOURCE_GAMEPAD, AKEY_EVENT_ACTION_DOWN, + event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_GAMEPAD), event.getSource()); + ASSERT_EQ(DISPLAY_ID, event.getDisplayId()); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); @@ -197,6 +201,11 @@ TEST_F(KeyEventTest, Properties) { // Set source. event.setSource(AINPUT_SOURCE_JOYSTICK); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource()); + + // Set display id. + constexpr int32_t newDisplayId = 2; + event.setDisplayId(newDisplayId); + ASSERT_EQ(newDisplayId, event.getDisplayId()); } @@ -248,7 +257,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); - event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, 0, + event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, @@ -301,6 +310,7 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); ASSERT_EQ(2, event->getDeviceId()); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_TOUCHSCREEN), event->getSource()); + ASSERT_EQ(DISPLAY_ID, event->getDisplayId()); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); @@ -434,6 +444,11 @@ TEST_F(MotionEventTest, Properties) { event.setSource(AINPUT_SOURCE_JOYSTICK); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource()); + // Set displayId. + constexpr int32_t newDisplayId = 2; + event.setDisplayId(newDisplayId); + ASSERT_EQ(newDisplayId, event.getDisplayId()); + // Set action. event.setAction(AMOTION_EVENT_ACTION_CANCEL); ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction()); @@ -557,7 +572,7 @@ TEST_F(MotionEventTest, Transform) { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; - event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0, + event.initialize(0, 0, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index c5322414fd..0788c89b2a 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -46,12 +46,12 @@ protected: virtual void TearDown() { if (mPublisher) { delete mPublisher; - mPublisher = NULL; + mPublisher = nullptr; } if (mConsumer) { delete mConsumer; - mConsumer = NULL; + mConsumer = nullptr; } serverChannel.clear(); @@ -70,32 +70,31 @@ TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { status_t status; - const uint32_t seq = 15; - const int32_t deviceId = 1; - const int32_t source = AINPUT_SOURCE_KEYBOARD; - const int32_t action = AKEY_EVENT_ACTION_DOWN; - const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; - const int32_t keyCode = AKEYCODE_ENTER; - const int32_t scanCode = 13; - const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; - const int32_t repeatCount = 1; - const nsecs_t downTime = 3; - const nsecs_t eventTime = 4; - - status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags, + constexpr uint32_t seq = 15; + constexpr int32_t deviceId = 1; + constexpr int32_t source = AINPUT_SOURCE_KEYBOARD; + constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr int32_t action = AKEY_EVENT_ACTION_DOWN; + constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; + constexpr int32_t keyCode = AKEYCODE_ENTER; + constexpr int32_t scanCode = 13; + constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + constexpr int32_t repeatCount = 1; + constexpr nsecs_t downTime = 3; + constexpr nsecs_t eventTime = 4; + + status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; uint32_t consumeSeq; InputEvent* event; - int32_t displayId; - status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event, - &displayId); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; - ASSERT_TRUE(event != NULL) + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType()) << "consumer should have returned a key event"; @@ -104,6 +103,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, keyEvent->getDeviceId()); EXPECT_EQ(source, keyEvent->getSource()); + EXPECT_EQ(displayId, keyEvent->getDisplayId()); EXPECT_EQ(action, keyEvent->getAction()); EXPECT_EQ(flags, keyEvent->getFlags()); EXPECT_EQ(keyCode, keyEvent->getKeyCode()); @@ -131,23 +131,23 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { status_t status; - const uint32_t seq = 15; - const int32_t deviceId = 1; - const int32_t source = AINPUT_SOURCE_TOUCHSCREEN; - int32_t displayId = 0; - const int32_t action = AMOTION_EVENT_ACTION_MOVE; - const int32_t actionButton = 0; - const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; - const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; - const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; - const float xOffset = -10; - const float yOffset = -20; - const float xPrecision = 0.25; - const float yPrecision = 0.5; - const nsecs_t downTime = 3; - const size_t pointerCount = 3; - const nsecs_t eventTime = 4; + constexpr uint32_t seq = 15; + constexpr int32_t deviceId = 1; + constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN; + constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE; + constexpr int32_t actionButton = 0; + constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; + constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; + constexpr float xOffset = -10; + constexpr float yOffset = -20; + constexpr float xPrecision = 0.25; + constexpr float yPrecision = 0.5; + constexpr nsecs_t downTime = 3; + constexpr size_t pointerCount = 3; + constexpr nsecs_t eventTime = 4; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { @@ -176,12 +176,11 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event, - &displayId); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; - ASSERT_TRUE(event != NULL) + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) << "consumer should have returned a motion event"; @@ -190,6 +189,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, motionEvent->getDeviceId()); EXPECT_EQ(source, motionEvent->getSource()); + EXPECT_EQ(displayId, motionEvent->getDisplayId()); EXPECT_EQ(action, motionEvent->getAction()); EXPECT_EQ(flags, motionEvent->getFlags()); EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp new file mode 100644 index 0000000000..5e5893fe4c --- /dev/null +++ b/libs/input/tests/InputWindow_test.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <binder/Binder.h> +#include <binder/Parcel.h> + +#include <input/InputWindow.h> +#include <input/InputTransport.h> + +namespace android { +namespace test { + +TEST(InputWindowInfo, ParcellingWithoutToken) { + InputWindowInfo i; + i.token = nullptr; + + Parcel p; + ASSERT_EQ(OK, i.write(p)); + p.setDataPosition(0); + InputWindowInfo i2 = InputWindowInfo::read(p); + ASSERT_TRUE(i2.token == nullptr); +} + +TEST(InputWindowInfo, Parcelling) { + InputWindowInfo i; + i.token = new BBinder(); + i.name = "Foobar"; + i.layoutParamsFlags = 7; + i.layoutParamsType = 39; + i.dispatchingTimeout = 12; + i.frameLeft = 93; + i.frameTop = 34; + i.frameRight = 16; + i.frameBottom = 19; + i.surfaceInset = 17; + i.globalScaleFactor = 0.3; + i.windowXScale = 0.4; + i.windowYScale = 0.5; + i.visible = false; + i.canReceiveKeys = false; + i.hasFocus = false; + i.hasWallpaper = false; + i.paused = false; + i.layer = 7; + i.ownerPid = 19; + i.ownerUid = 24; + i.inputFeatures = 29; + i.displayId = 34; + + Parcel p; + i.write(p); + + p.setDataPosition(0); + InputWindowInfo i2 = InputWindowInfo::read(p); + ASSERT_EQ(i.token, i2.token); + ASSERT_EQ(i.name, i2.name); + ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags); + ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); + ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout); + ASSERT_EQ(i.frameLeft, i2.frameLeft); + ASSERT_EQ(i.frameTop, i2.frameTop); + ASSERT_EQ(i.frameRight, i2.frameRight); + ASSERT_EQ(i.frameBottom, i2.frameBottom); + ASSERT_EQ(i.surfaceInset, i2.surfaceInset); + ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor); + ASSERT_EQ(i.windowXScale, i2.windowXScale); + ASSERT_EQ(i.windowYScale, i2.windowYScale); + ASSERT_EQ(i.visible, i2.visible); + ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys); + ASSERT_EQ(i.hasFocus, i2.hasFocus); + ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper); + ASSERT_EQ(i.paused, i2.paused); + ASSERT_EQ(i.layer, i2.layer); + ASSERT_EQ(i.ownerPid, i2.ownerPid); + ASSERT_EQ(i.ownerUid, i2.ownerUid); + ASSERT_EQ(i.inputFeatures, i2.inputFeatures); + ASSERT_EQ(i.displayId, i2.displayId); +} + +} // namespace test +} // namespace android diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index d19f3b8066..12a67828ac 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -65,6 +65,9 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76); CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 80); CHECK_OFFSET(InputMessage::Body::Motion, pointers, 88); + + CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); + CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); } } // namespace android diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 9da2e2a315..af97c34055 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -27,6 +27,8 @@ using android::base::StringPrintf; namespace android { +constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; // default display id + constexpr int32_t DEFAULT_POINTER_ID = 0; // pointer ID used for manually defined tests // velocity must be in the range (1-tol)*EV <= velocity <= (1+tol)*EV @@ -96,7 +98,7 @@ MotionEvent* createSimpleMotionEvent(const Position* positions, size_t numSample // First sample added separately with initialize coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y); - event->initialize(0, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, + event->initialize(0, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, positions[0].time, 1, properties, &coords); for (size_t i = 1; i < numSamples; i++) { diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index 7e26b0b587..a19fe17d16 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -41,33 +41,11 @@ using namespace android; // ---------------------------------------------------------------------------- int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) { - if (!outBuffer || !desc) - return BAD_VALUE; - - if (!AHardwareBuffer_isValidPixelFormat(desc->format)) { - ALOGE("Invalid AHardwareBuffer pixel format %u (%#x))", desc->format, desc->format); - return BAD_VALUE; - } + if (!outBuffer || !desc) return BAD_VALUE; + if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) return BAD_VALUE; int format = AHardwareBuffer_convertToPixelFormat(desc->format); - if (desc->rfu0 != 0 || desc->rfu1 != 0) { - ALOGE("AHardwareBuffer_Desc::rfu fields must be 0"); - return BAD_VALUE; - } - - if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) { - ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB format"); - return BAD_VALUE; - } - - if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) && - (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) { - ALOGE("AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER " - "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER"); - return BAD_VALUE; - } - - uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); + uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); sp<GraphicBuffer> gbuffer(new GraphicBuffer( desc->width, desc->height, format, desc->layers, usage, std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]")); @@ -122,19 +100,26 @@ int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) { ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " - " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); + "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); return BAD_VALUE; } usage = AHardwareBuffer_convertToGrallocUsageBits(usage); - GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + + if (gbuffer->getLayerCount() > 1) { + ALOGE("Buffer with multiple layers passed to AHardwareBuffer_lock; " + "only buffers with one layer are allowed"); + return INVALID_OPERATION; + } + Rect bounds; if (!rect) { - bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight())); + bounds.set(Rect(gbuffer->getWidth(), gbuffer->getHeight())); } else { bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom)); } - return gBuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence); + return gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence); } int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) { @@ -274,6 +259,25 @@ int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** out return NO_ERROR; } +int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) { + if (!desc) return 0; + if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0; + + // Make a trial allocation. + // TODO(b/115660272): add implementation that uses a HAL query. + AHardwareBuffer_Desc trialDesc = *desc; + trialDesc.width = 4; + trialDesc.height = desc->format == AHARDWAREBUFFER_FORMAT_BLOB ? 1 : 4; + trialDesc.layers = desc->layers == 1 ? 1 : 2; + AHardwareBuffer* trialBuffer = nullptr; + int result = AHardwareBuffer_allocate(&trialDesc, &trialBuffer); + if (result == NO_ERROR) { + AHardwareBuffer_release(trialBuffer); + return 1; + } + return 0; +} + // ---------------------------------------------------------------------------- // VNDK functions @@ -322,14 +326,71 @@ int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, namespace android { -// A 1:1 mapping of AHardwaqreBuffer bitmasks to gralloc1 bitmasks. -struct UsageMaskMapping { - uint64_t hardwareBufferMask; - uint64_t grallocMask; -}; +bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log) { + if (desc->width == 0 || desc->height == 0 || desc->layers == 0) { + ALOGE_IF(log, "Width, height and layers must all be nonzero"); + return false; + } + + if (!AHardwareBuffer_isValidPixelFormat(desc->format)) { + ALOGE_IF(log, "Invalid AHardwareBuffer pixel format %u (%#x))", + desc->format, desc->format); + return false; + } + + if (desc->rfu0 != 0 || desc->rfu1 != 0) { + ALOGE_IF(log, "AHardwareBuffer_Desc::rfu fields must be 0"); + return false; + } + + if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB) { + if (desc->height != 1 || desc->layers != 1) { + ALOGE_IF(log, "Height and layers must be 1 for AHARDWAREBUFFER_FORMAT_BLOB"); + return false; + } + const uint64_t blobInvalidGpuMask = + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | + AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | + AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE | + AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP; + if (desc->usage & blobInvalidGpuMask) { + ALOGE_IF(log, "Invalid GPU usage flag for AHARDWAREBUFFER_FORMAT_BLOB; " + "only AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER is allowed"); + return false; + } + if (desc->usage & AHARDWAREBUFFER_USAGE_VIDEO_ENCODE) { + ALOGE_IF(log, "AHARDWAREBUFFER_FORMAT_BLOB cannot be encoded as video"); + return false; + } + } else { + if (desc->usage & AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA) { + ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA requires AHARDWAREBUFFER_FORMAT_BLOB"); + return false; + } + if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) { + ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER requires AHARDWAREBUFFER_FORMAT_BLOB"); + return false; + } + } + + if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) && + (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) { + ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER " + "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER"); + return false; + } -static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) { - return (mask & bitsToCheck) == bitsToCheck && bitsToCheck; + if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) { + if (desc->width != desc->height) { + ALOGE_IF(log, "Cube maps must be square"); + return false; + } + if (desc->layers % 6 != 0) { + ALOGE_IF(log, "Cube map layers must be a multiple of 6"); + return false; + } + } + return true; } bool AHardwareBuffer_isValidPixelFormat(uint32_t format) { @@ -445,7 +506,7 @@ uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) { "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE == (uint64_t)BufferUsage::GPU_TEXTURE, "gralloc and AHardwareBuffer flags don't match"); - static_assert(AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT == (uint64_t)BufferUsage::GPU_RENDER_TARGET, + static_assert(AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER == (uint64_t)BufferUsage::GPU_RENDER_TARGET, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT == (uint64_t)BufferUsage::PROTECTED, "gralloc and AHardwareBuffer flags don't match"); diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index d74bdb3e99..d8478848cb 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -13,13 +13,20 @@ // limitations under the License. ndk_headers { - name: "libnativewindow_headers", + name: "libnativewindow_ndk_headers", from: "include/android", to: "android", srcs: ["include/android/*.h"], license: "NOTICE", } +// TODO(b/118715870): cleanup header files +cc_library_headers { + name: "libnativewindow_headers", + export_include_dirs: ["include"], + vendor_available: false, +} + ndk_library { name: "libnativewindow", symbol_file: "libnativewindow.map.txt", @@ -40,6 +47,7 @@ cc_library { cflags: [ "-Wall", "-Werror", + "-Wno-enum-compare", "-Wno-unused-function", ], @@ -66,6 +74,7 @@ cc_library { header_libs: [ "libnativebase_headers", + "libnativewindow_headers", ], // headers we include in our public headers diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h index 71f563467d..bf688f8ef8 100644 --- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h +++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h @@ -28,10 +28,15 @@ #include <stdint.h> struct AHardwareBuffer; +struct AHardwareBuffer_Desc; struct ANativeWindowBuffer; namespace android { +// Validates whether the passed description does not have conflicting +// parameters. Note: this does not verify any platform-specific contraints. +bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log); + // whether this AHardwareBuffer format is valid bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format); diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index 52b4582466..03545a68ca 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -194,6 +194,8 @@ enum AHardwareBuffer_UsageFlags { /// The buffer will be read from by the GPU as a texture. AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8, + /// The buffer will be written to by the GPU as a framebuffer attachment. + AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 1UL << 9, /** * The buffer will be written to by the GPU as a framebuffer * attachment. @@ -201,9 +203,10 @@ enum AHardwareBuffer_UsageFlags { * Note that the name of this flag is somewhat misleading: it does * not imply that the buffer contains a color format. A buffer with * depth or stencil format that will be used as a framebuffer - * attachment should also have this flag. + * attachment should also have this flag. Use the equivalent flag + * AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER to avoid this confusion. */ - AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = 1UL << 9, + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, /** * The buffer is protected from direct CPU access or being read by * non-secure hardware, such as video encoders. @@ -309,7 +312,7 @@ int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) __INTRODUCED_IN(26); /** * Acquire a reference on the given AHardwareBuffer object. - * + * * This prevents the object from being deleted until the last reference * is removed. */ @@ -417,6 +420,29 @@ int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** out #endif // __ANDROID_API__ >= 26 +#if __ANDROID_API__ >= 29 + +/** + * Test whether the given format and usage flag combination is + * allocatable. + * + * If this function returns true, it means that a buffer with the given + * description can be allocated on this implementation, unless resource + * exhaustion occurs. If this function returns false, it means that the + * allocation of the given description will never succeed. + * + * The return value of this function may depend on all fields in the + * description, except stride, which is always ignored. For example, + * some implementations have implementation-defined limits on texture + * size and layer count. + * + * \return 1 if the format and usage flag combination is allocatable, + * 0 otherwise. + */ +int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_IN(29); + +#endif // __ANDROID_API__ >= 29 + __END_DECLS #endif // ANDROID_HARDWARE_BUFFER_H diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 197f73f3b1..61590e0196 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -202,7 +202,7 @@ enum { * ANativeWindow. */ enum { -// clang-format off + // clang-format off NATIVE_WINDOW_SET_USAGE = 0, /* deprecated */ NATIVE_WINDOW_CONNECT = 1, /* deprecated */ NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */ @@ -237,7 +237,8 @@ enum { NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31, NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32, NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33, -// clang-format on + NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34, + // clang-format on }; /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */ @@ -748,6 +749,27 @@ static inline int native_window_set_buffers_cta861_3_metadata( } /* + * native_window_set_buffers_hdr10_plus_metadata(..., metadata) + * All buffers queued after this call will be associated with the + * HDR10+ dynamic metadata specified. + * + * metadata specifies additional dynamic information about the + * contents of the buffer that may affect how it is displayed. When + * it is nullptr, it means no such information is available. No + * HDR10+ dynamic emtadata is associated with the buffers by default. + * + * Parameter "size" refers to the length of the metadata blob pointed to + * by parameter "data". The metadata blob will adhere to the HDR10+ SEI + * message standard. + */ +static inline int native_window_set_buffers_hdr10_plus_metadata(struct ANativeWindow* window, + const size_t size, + const uint8_t* metadata) { + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA, size, + metadata); +} + +/* * native_window_set_buffers_transform(..., int transform) * All buffers queued after this call will be displayed transformed according * to the transform parameter specified. diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 753954d97a..a796e97e29 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -5,6 +5,7 @@ LIBNATIVEWINDOW { AHardwareBuffer_createFromHandle; # vndk AHardwareBuffer_describe; AHardwareBuffer_getNativeHandle; # vndk + AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; AHardwareBuffer_recvHandleFromUnixSocket; AHardwareBuffer_release; diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp new file mode 100644 index 0000000000..d872f0264e --- /dev/null +++ b/libs/renderengine/Android.bp @@ -0,0 +1,81 @@ +cc_defaults { + name: "renderengine_defaults", + cflags: [ + "-DLOG_TAG=\"RenderEngine\"", + "-Wall", + "-Werror", + "-Wthread-safety", + "-Wunused", + "-Wunreachable-code", + ], +} + +cc_defaults { + name: "librenderengine_defaults", + defaults: ["renderengine_defaults"], + cflags: [ + "-DGL_GLEXT_PROTOTYPES", + "-DEGL_EGLEXT_PROTOTYPES", + ], + shared_libs: [ + "libbase", + "libcutils", + "libEGL", + "libGLESv1_CM", + "libGLESv2", + "libgui", + "liblog", + "libnativewindow", + "libui", + "libutils", + ], + local_include_dirs: ["include"], + export_include_dirs: ["include"], +} + +filegroup { + name: "librenderengine_sources", + srcs: [ + "Description.cpp", + "Mesh.cpp", + "RenderEngine.cpp", + "Texture.cpp", + ], +} + +filegroup { + name: "librenderengine_gl_sources", + srcs: [ + "gl/GLESRenderEngine.cpp", + "gl/GLExtensions.cpp", + "gl/GLFramebuffer.cpp", + "gl/GLImage.cpp", + "gl/Program.cpp", + "gl/ProgramCache.cpp", + ], +} + +cc_library_static { + name: "librenderengine", + defaults: ["librenderengine_defaults"], + vendor_available: true, + vndk: { + enabled: true, + }, + double_loadable: true, + clang: true, + cflags: [ + "-fvisibility=hidden", + "-Werror=format", + ], + cppflags: [ + "-fwhole-program-vtables", // requires ThinLTO + ], + srcs: [ + ":librenderengine_sources", + ":librenderengine_gl_sources", + ], + lto: { + thin: true, + }, +} diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp new file mode 100644 index 0000000000..b9cea1071f --- /dev/null +++ b/libs/renderengine/Description.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2013 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 <renderengine/private/Description.h> + +#include <stdint.h> + +#include <utils/TypeHelpers.h> + +namespace android { +namespace renderengine { + +Description::TransferFunction Description::dataSpaceToTransferFunction(ui::Dataspace dataSpace) { + ui::Dataspace transfer = static_cast<ui::Dataspace>(dataSpace & ui::Dataspace::TRANSFER_MASK); + switch (transfer) { + case ui::Dataspace::TRANSFER_ST2084: + return Description::TransferFunction::ST2084; + case ui::Dataspace::TRANSFER_HLG: + return Description::TransferFunction::HLG; + case ui::Dataspace::TRANSFER_LINEAR: + return Description::TransferFunction::LINEAR; + default: + return Description::TransferFunction::SRGB; + } +} + +bool Description::hasInputTransformMatrix() const { + const mat4 identity; + return inputTransformMatrix != identity; +} + +bool Description::hasOutputTransformMatrix() const { + const mat4 identity; + return outputTransformMatrix != identity; +} + +bool Description::hasColorMatrix() const { + const mat4 identity; + return colorMatrix != identity; +} + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp new file mode 100644 index 0000000000..f5387f28ea --- /dev/null +++ b/libs/renderengine/Mesh.cpp @@ -0,0 +1,104 @@ +/* + * Copyright 2013 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 <renderengine/Mesh.h> + +#include <utils/Log.h> + +namespace android { +namespace renderengine { + +Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize) + : mVertexCount(vertexCount), + mVertexSize(vertexSize), + mTexCoordsSize(texCoordSize), + mPrimitive(primitive) { + if (vertexCount == 0) { + mVertices.resize(1); + mVertices[0] = 0.0f; + mStride = 0; + return; + } + + const size_t CROP_COORD_SIZE = 2; + size_t stride = vertexSize + texCoordSize + CROP_COORD_SIZE; + size_t remainder = (stride * vertexCount) / vertexCount; + // Since all of the input parameters are unsigned, if stride is less than + // either vertexSize or texCoordSize, it must have overflowed. remainder + // will be equal to stride as long as stride * vertexCount doesn't overflow. + if ((stride < vertexSize) || (remainder != stride)) { + ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize, + CROP_COORD_SIZE); + mVertices.resize(1); + mVertices[0] = 0.0f; + mVertexCount = 0; + mVertexSize = 0; + mTexCoordsSize = 0; + mStride = 0; + return; + } + + mVertices.resize(stride * vertexCount); + mStride = stride; +} + +Mesh::Primitive Mesh::getPrimitive() const { + return mPrimitive; +} + +float const* Mesh::getPositions() const { + return mVertices.data(); +} +float* Mesh::getPositions() { + return mVertices.data(); +} + +float const* Mesh::getTexCoords() const { + return mVertices.data() + mVertexSize; +} +float* Mesh::getTexCoords() { + return mVertices.data() + mVertexSize; +} + +float const* Mesh::getCropCoords() const { + return mVertices.data() + mVertexSize + mTexCoordsSize; +} +float* Mesh::getCropCoords() { + return mVertices.data() + mVertexSize + mTexCoordsSize; +} + +size_t Mesh::getVertexCount() const { + return mVertexCount; +} + +size_t Mesh::getVertexSize() const { + return mVertexSize; +} + +size_t Mesh::getTexCoordsSize() const { + return mTexCoordsSize; +} + +size_t Mesh::getByteStride() const { + return mStride * sizeof(float); +} + +size_t Mesh::getStride() const { + return mStride; +} + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp new file mode 100644 index 0000000000..6dd7283a15 --- /dev/null +++ b/libs/renderengine/RenderEngine.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2013 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 <renderengine/RenderEngine.h> + +#include <cutils/properties.h> +#include <log/log.h> +#include <private/gui/SyncFeatures.h> +#include "gl/GLESRenderEngine.h" + +namespace android { +namespace renderengine { + +std::unique_ptr<impl::RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags) { + char prop[PROPERTY_VALUE_MAX]; + property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles"); + if (strcmp(prop, "gles") == 0) { + ALOGD("RenderEngine GLES Backend"); + return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags); + } + ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop); + return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags); +} + +RenderEngine::~RenderEngine() = default; + +namespace impl { + +RenderEngine::RenderEngine(uint32_t featureFlags) : mFeatureFlags(featureFlags) {} + +RenderEngine::~RenderEngine() = default; + +bool RenderEngine::useNativeFenceSync() const { + return SyncFeatures::getInstance().useNativeFenceSync(); +} + +bool RenderEngine::useWaitSync() const { + return SyncFeatures::getInstance().useWaitSync(); +} + +} // namespace impl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/TEST_MAPPING b/libs/renderengine/TEST_MAPPING new file mode 100644 index 0000000000..995dba1422 --- /dev/null +++ b/libs/renderengine/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "librenderengine_test" + } + ] +} diff --git a/libs/renderengine/Texture.cpp b/libs/renderengine/Texture.cpp new file mode 100644 index 0000000000..154cde80b9 --- /dev/null +++ b/libs/renderengine/Texture.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2013 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 <renderengine/Texture.h> + +namespace android { +namespace renderengine { + +Texture::Texture() + : mTextureName(0), mTextureTarget(TEXTURE_2D), mWidth(0), mHeight(0), mFiltering(false) {} + +Texture::Texture(Target textureTarget, uint32_t textureName) + : mTextureName(textureName), + mTextureTarget(textureTarget), + mWidth(0), + mHeight(0), + mFiltering(false) {} + +void Texture::init(Target textureTarget, uint32_t textureName) { + mTextureName = textureName; + mTextureTarget = textureTarget; +} + +Texture::~Texture() {} + +void Texture::setMatrix(float const* matrix) { + mTextureMatrix = mat4(matrix); +} + +void Texture::setFiltering(bool enabled) { + mFiltering = enabled; +} + +void Texture::setDimensions(size_t width, size_t height) { + mWidth = width; + mHeight = height; +} + +uint32_t Texture::getTextureName() const { + return mTextureName; +} + +uint32_t Texture::getTextureTarget() const { + return mTextureTarget; +} + +const mat4& Texture::getMatrix() const { + return mTextureMatrix; +} + +bool Texture::getFiltering() const { + return mFiltering; +} + +size_t Texture::getWidth() const { + return mWidth; +} + +size_t Texture::getHeight() const { + return mHeight; +} + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp new file mode 100644 index 0000000000..51cf1886d3 --- /dev/null +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -0,0 +1,1133 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "GLESRenderEngine.h" + +#include <math.h> +#include <fstream> +#include <sstream> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <android-base/stringprintf.h> +#include <cutils/compiler.h> +#include <renderengine/Mesh.h> +#include <renderengine/Texture.h> +#include <renderengine/private/Description.h> +#include <ui/ColorSpace.h> +#include <ui/DebugUtils.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <utils/KeyedVector.h> +#include <utils/Trace.h> +#include "GLExtensions.h" +#include "GLFramebuffer.h" +#include "GLImage.h" +#include "Program.h" +#include "ProgramCache.h" + +extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); + +bool checkGlError(const char* op, int lineNumber) { + bool errorFound = false; + GLint error = glGetError(); + while (error != GL_NO_ERROR) { + errorFound = true; + error = glGetError(); + ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error); + } + return errorFound; +} + +static constexpr bool outputDebugPPMs = false; + +void writePPM(const char* basename, GLuint width, GLuint height) { + ALOGV("writePPM #%s: %d x %d", basename, width, height); + + std::vector<GLubyte> pixels(width * height * 4); + std::vector<GLubyte> outBuffer(width * height * 3); + + // TODO(courtneygo): We can now have float formats, need + // to remove this code or update to support. + // Make returned pixels fit in uint32_t, one byte per component + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); + if (checkGlError(__FUNCTION__, __LINE__)) { + return; + } + + std::string filename(basename); + filename.append(".ppm"); + std::ofstream file(filename.c_str(), std::ios::binary); + if (!file.is_open()) { + ALOGE("Unable to open file: %s", filename.c_str()); + ALOGE("You may need to do: \"adb shell setenforce 0\" to enable " + "surfaceflinger to write debug images"); + return; + } + + file << "P6\n"; + file << width << "\n"; + file << height << "\n"; + file << 255 << "\n"; + + auto ptr = reinterpret_cast<char*>(pixels.data()); + auto outPtr = reinterpret_cast<char*>(outBuffer.data()); + for (int y = height - 1; y >= 0; y--) { + char* data = ptr + y * width * sizeof(uint32_t); + + for (GLuint x = 0; x < width; x++) { + // Only copy R, G and B components + outPtr[0] = data[0]; + outPtr[1] = data[1]; + outPtr[2] = data[2]; + data += sizeof(uint32_t); + outPtr += 3; + } + } + file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size()); +} + +namespace android { +namespace renderengine { +namespace gl { + +using base::StringAppendF; +using ui::Dataspace; + +static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute, + EGLint wanted, EGLConfig* outConfig) { + EGLint numConfigs = -1, n = 0; + eglGetConfigs(dpy, nullptr, 0, &numConfigs); + std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR); + eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n); + configs.resize(n); + + if (!configs.empty()) { + if (attribute != EGL_NONE) { + for (EGLConfig config : configs) { + EGLint value = 0; + eglGetConfigAttrib(dpy, config, attribute, &value); + if (wanted == value) { + *outConfig = config; + return NO_ERROR; + } + } + } else { + // just pick the first one + *outConfig = configs[0]; + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +class EGLAttributeVector { + struct Attribute; + class Adder; + friend class Adder; + KeyedVector<Attribute, EGLint> mList; + struct Attribute { + Attribute() : v(0){}; + explicit Attribute(EGLint v) : v(v) {} + EGLint v; + bool operator<(const Attribute& other) const { + // this places EGL_NONE at the end + EGLint lhs(v); + EGLint rhs(other.v); + if (lhs == EGL_NONE) lhs = 0x7FFFFFFF; + if (rhs == EGL_NONE) rhs = 0x7FFFFFFF; + return lhs < rhs; + } + }; + class Adder { + friend class EGLAttributeVector; + EGLAttributeVector& v; + EGLint attribute; + Adder(EGLAttributeVector& v, EGLint attribute) : v(v), attribute(attribute) {} + + public: + void operator=(EGLint value) { + if (attribute != EGL_NONE) { + v.mList.add(Attribute(attribute), value); + } + } + operator EGLint() const { return v.mList[attribute]; } + }; + +public: + EGLAttributeVector() { mList.add(Attribute(EGL_NONE), EGL_NONE); } + void remove(EGLint attribute) { + if (attribute != EGL_NONE) { + mList.removeItem(Attribute(attribute)); + } + } + Adder operator[](EGLint attribute) { return Adder(*this, attribute); } + EGLint operator[](EGLint attribute) const { return mList[attribute]; } + // cast-operator to (EGLint const*) + operator EGLint const*() const { return &mList.keyAt(0).v; } +}; + +static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType, + EGLConfig* config) { + // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if + // it is to be used with WIFI displays + status_t err; + EGLint wantedAttribute; + EGLint wantedAttributeValue; + + EGLAttributeVector attribs; + if (renderableType) { + attribs[EGL_RENDERABLE_TYPE] = renderableType; + attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE; + attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT; + attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE; + attribs[EGL_RED_SIZE] = 8; + attribs[EGL_GREEN_SIZE] = 8; + attribs[EGL_BLUE_SIZE] = 8; + attribs[EGL_ALPHA_SIZE] = 8; + wantedAttribute = EGL_NONE; + wantedAttributeValue = EGL_NONE; + } else { + // if no renderable type specified, fallback to a simplified query + wantedAttribute = EGL_NATIVE_VISUAL_ID; + wantedAttributeValue = format; + } + + err = selectConfigForAttribute(display, attribs, wantedAttribute, wantedAttributeValue, config); + if (err == NO_ERROR) { + EGLint caveat; + if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat)) + ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!"); + } + + return err; +} + +std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(int hwcFormat, uint32_t featureFlags) { + // initialize EGL for the default display + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!eglInitialize(display, nullptr, nullptr)) { + LOG_ALWAYS_FATAL("failed to initialize EGL"); + } + + GLExtensions& extensions = GLExtensions::getInstance(); + extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION), + eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS)); + + // The code assumes that ES2 or later is available if this extension is + // supported. + EGLConfig config = EGL_NO_CONFIG; + if (!extensions.hasNoConfigContext()) { + config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); + } + + bool useContextPriority = extensions.hasContextPriority() && + (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT); + EGLContext protectedContext = EGL_NO_CONTEXT; + if (extensions.hasProtectedContent()) { + protectedContext = createEglContext(display, config, nullptr, useContextPriority, + Protection::PROTECTED); + ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context"); + } + + EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority, + Protection::UNPROTECTED); + + // if can't create a GL context, we can only abort. + LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed"); + + EGLSurface dummy = EGL_NO_SURFACE; + if (!extensions.hasSurfacelessContext()) { + dummy = createDummyEglPbufferSurface(display, config, hwcFormat, Protection::UNPROTECTED); + LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer"); + } + EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt); + LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current"); + extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER), + glGetString(GL_VERSION), glGetString(GL_EXTENSIONS)); + + // In order to have protected contents in GPU composition, the OpenGL ES extension + // GL_EXT_protected_textures must be supported. If it's not supported, reset + // protected context to EGL_NO_CONTEXT to indicate that protected contents is not supported. + if (!extensions.hasProtectedTexture()) { + protectedContext = EGL_NO_CONTEXT; + } + + EGLSurface protectedDummy = EGL_NO_SURFACE; + if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) { + protectedDummy = + createDummyEglPbufferSurface(display, config, hwcFormat, Protection::PROTECTED); + ALOGE_IF(protectedDummy == EGL_NO_SURFACE, "can't create protected dummy pbuffer"); + } + + // now figure out what version of GL did we actually get + GlesVersion version = parseGlesVersion(extensions.getVersion()); + + // initialize the renderer while GL is current + std::unique_ptr<GLESRenderEngine> engine; + switch (version) { + case GLES_VERSION_1_0: + case GLES_VERSION_1_1: + LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run."); + break; + case GLES_VERSION_2_0: + case GLES_VERSION_3_0: + engine = std::make_unique<GLESRenderEngine>(featureFlags, display, config, ctxt, dummy, + protectedContext, protectedDummy); + break; + } + + ALOGI("OpenGL ES informations:"); + ALOGI("vendor : %s", extensions.getVendor()); + ALOGI("renderer : %s", extensions.getRenderer()); + ALOGI("version : %s", extensions.getVersion()); + ALOGI("extensions: %s", extensions.getExtensions()); + ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize()); + ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims()); + + return engine; +} + +EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) { + status_t err; + EGLConfig config; + + // First try to get an ES3 config + err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config); + if (err != NO_ERROR) { + // If ES3 fails, try to get an ES2 config + err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config); + if (err != NO_ERROR) { + // If ES2 still doesn't work, probably because we're on the emulator. + // try a simplified query + ALOGW("no suitable EGLConfig found, trying a simpler query"); + err = selectEGLConfig(display, format, 0, &config); + if (err != NO_ERROR) { + // this EGL is too lame for android + LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); + } + } + } + + if (logConfig) { + // print some debugging info + EGLint r, g, b, a; + eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); + eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); + ALOGI("EGL information:"); + ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); + ALOGI("version : %s", eglQueryString(display, EGL_VERSION)); + ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS)); + ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); + ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + } + + return config; +} + +GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EGLConfig config, + EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext, + EGLSurface protectedDummy) + : renderengine::impl::RenderEngine(featureFlags), + mEGLDisplay(display), + mEGLConfig(config), + mEGLContext(ctxt), + mDummySurface(dummy), + mProtectedEGLContext(protectedContext), + mProtectedDummySurface(protectedDummy), + mVpWidth(0), + mVpHeight(0), + mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) { + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + + // Initialize protected EGL Context. + if (mProtectedEGLContext != EGL_NO_CONTEXT) { + EGLBoolean success = eglMakeCurrent(display, mProtectedDummySurface, mProtectedDummySurface, + mProtectedEGLContext); + ALOGE_IF(!success, "can't make protected context current"); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + success = eglMakeCurrent(display, mDummySurface, mDummySurface, mEGLContext); + LOG_ALWAYS_FATAL_IF(!success, "can't make default context current"); + } + + const uint16_t protTexData[] = {0}; + glGenTextures(1, &mProtectedTexName); + glBindTexture(GL_TEXTURE_2D, mProtectedTexName); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData); + + // mColorBlindnessCorrection = M; + + if (mUseColorManagement) { + const ColorSpace srgb(ColorSpace::sRGB()); + const ColorSpace displayP3(ColorSpace::DisplayP3()); + const ColorSpace bt2020(ColorSpace::BT2020()); + + // no chromatic adaptation needed since all color spaces use D65 for their white points. + mSrgbToXyz = mat4(srgb.getRGBtoXYZ()); + mDisplayP3ToXyz = mat4(displayP3.getRGBtoXYZ()); + mBt2020ToXyz = mat4(bt2020.getRGBtoXYZ()); + mXyzToSrgb = mat4(srgb.getXYZtoRGB()); + mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB()); + mXyzToBt2020 = mat4(bt2020.getXYZtoRGB()); + + // Compute sRGB to Display P3 and BT2020 transform matrix. + // NOTE: For now, we are limiting output wide color space support to + // Display-P3 and BT2020 only. + mSrgbToDisplayP3 = mXyzToDisplayP3 * mSrgbToXyz; + mSrgbToBt2020 = mXyzToBt2020 * mSrgbToXyz; + + // Compute Display P3 to sRGB and BT2020 transform matrix. + mDisplayP3ToSrgb = mXyzToSrgb * mDisplayP3ToXyz; + mDisplayP3ToBt2020 = mXyzToBt2020 * mDisplayP3ToXyz; + + // Compute BT2020 to sRGB and Display P3 transform matrix + mBt2020ToSrgb = mXyzToSrgb * mBt2020ToXyz; + mBt2020ToDisplayP3 = mXyzToDisplayP3 * mBt2020ToXyz; + } +} + +GLESRenderEngine::~GLESRenderEngine() { + eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mEGLDisplay); +} + +std::unique_ptr<Framebuffer> GLESRenderEngine::createFramebuffer() { + return std::make_unique<GLFramebuffer>(*this); +} + +std::unique_ptr<Image> GLESRenderEngine::createImage() { + return std::make_unique<GLImage>(*this); +} + +void GLESRenderEngine::primeCache() const { + ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext, + mFeatureFlags & USE_COLOR_MANAGEMENT); +} + +bool GLESRenderEngine::isCurrent() const { + return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext(); +} + +base::unique_fd GLESRenderEngine::flush() { + if (!GLExtensions::getInstance().hasNativeFenceSync()) { + return base::unique_fd(); + } + + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGW("failed to create EGL native fence sync: %#x", eglGetError()); + return base::unique_fd(); + } + + // native fence fd will not be populated until flush() is done. + glFlush(); + + // get the fence fd + base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync)); + eglDestroySyncKHR(mEGLDisplay, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ALOGW("failed to dup EGL native fence sync: %#x", eglGetError()); + } + + return fenceFd; +} + +bool GLESRenderEngine::finish() { + if (!GLExtensions::getInstance().hasFenceSync()) { + ALOGW("no synchronization support"); + return false; + } + + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGW("failed to create EGL fence sync: %#x", eglGetError()); + return false; + } + + EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, + 2000000000 /*2 sec*/); + EGLint error = eglGetError(); + eglDestroySyncKHR(mEGLDisplay, sync); + if (result != EGL_CONDITION_SATISFIED_KHR) { + if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGW("fence wait timed out"); + } else { + ALOGW("error waiting on EGL fence: %#x", error); + } + return false; + } + + return true; +} + +bool GLESRenderEngine::waitFence(base::unique_fd fenceFd) { + if (!GLExtensions::getInstance().hasNativeFenceSync() || + !GLExtensions::getInstance().hasWaitSync()) { + return false; + } + + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + ALOGE("failed to create EGL native fence sync: %#x", eglGetError()); + return false; + } + + // fenceFd is now owned by EGLSync + (void)fenceFd.release(); + + // XXX: The spec draft is inconsistent as to whether this should return an + // EGLint or void. Ignore the return value for now, as it's not strictly + // needed. + eglWaitSyncKHR(mEGLDisplay, sync, 0); + EGLint error = eglGetError(); + eglDestroySyncKHR(mEGLDisplay, sync); + if (error != EGL_SUCCESS) { + ALOGE("failed to wait for EGL native fence sync: %#x", error); + return false; + } + + return true; +} + +void GLESRenderEngine::clearWithColor(float red, float green, float blue, float alpha) { + glClearColor(red, green, blue, alpha); + glClear(GL_COLOR_BUFFER_BIT); +} + +void GLESRenderEngine::fillRegionWithColor(const Region& region, float red, float green, float blue, + float alpha) { + size_t c; + Rect const* r = region.getArray(&c); + Mesh mesh(Mesh::TRIANGLES, c * 6, 2); + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + for (size_t i = 0; i < c; i++, r++) { + position[i * 6 + 0].x = r->left; + position[i * 6 + 0].y = r->top; + position[i * 6 + 1].x = r->left; + position[i * 6 + 1].y = r->bottom; + position[i * 6 + 2].x = r->right; + position[i * 6 + 2].y = r->bottom; + position[i * 6 + 3].x = r->left; + position[i * 6 + 3].y = r->top; + position[i * 6 + 4].x = r->right; + position[i * 6 + 4].y = r->bottom; + position[i * 6 + 5].x = r->right; + position[i * 6 + 5].y = r->top; + } + setupFillWithColor(red, green, blue, alpha); + drawMesh(mesh); +} + +void GLESRenderEngine::setScissor(const Rect& region) { + // Invert y-coordinate to map to GL-space. + int32_t canvasHeight = mFboHeight; + int32_t glBottom = canvasHeight - region.bottom; + + glScissor(region.left, glBottom, region.getWidth(), region.getHeight()); + glEnable(GL_SCISSOR_TEST); +} + +void GLESRenderEngine::disableScissor() { + glDisable(GL_SCISSOR_TEST); +} + +void GLESRenderEngine::genTextures(size_t count, uint32_t* names) { + glGenTextures(count, names); +} + +void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) { + glDeleteTextures(count, names); +} + +void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) { + const GLImage& glImage = static_cast<const GLImage&>(image); + const GLenum target = GL_TEXTURE_EXTERNAL_OES; + + glBindTexture(target, texName); + if (supportsProtectedContent()) { + glTexParameteri(target, GL_TEXTURE_PROTECTED_EXT, + glImage.isProtected() ? GL_TRUE : GL_FALSE); + } + if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) { + glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage())); + } +} + +status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) { + GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer); + EGLImageKHR eglImage = glFramebuffer->getEGLImage(); + uint32_t textureName = glFramebuffer->getTextureName(); + uint32_t framebufferName = glFramebuffer->getFramebufferName(); + + // Bind the texture and turn our EGLImage into a texture + glBindTexture(GL_TEXTURE_2D, textureName); + if (supportsProtectedContent()) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_PROTECTED_EXT, + mInProtectedContext ? GL_TRUE : GL_FALSE); + } + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage); + + // Bind the Framebuffer to render into + glBindFramebuffer(GL_FRAMEBUFFER, framebufferName); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0); + + mFboHeight = glFramebuffer->getBufferHeight(); + + uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d", + glStatus); + + return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE; +} + +void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) { + mFboHeight = 0; + + // back to main framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void GLESRenderEngine::checkErrors() const { + do { + // there could be more than one error flag + GLenum error = glGetError(); + if (error == GL_NO_ERROR) break; + ALOGE("GL error 0x%04x", int(error)); + } while (true); +} + +bool GLESRenderEngine::supportsProtectedContent() const { + return mProtectedEGLContext != EGL_NO_CONTEXT; +} + +bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) { + if (useProtectedContext == mInProtectedContext) { + return true; + } + if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) { + return false; + } + const EGLSurface surface = useProtectedContext ? mProtectedDummySurface : mDummySurface; + const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext; + const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE; + if (success) { + mInProtectedContext = useProtectedContext; + } + return success; +} + +status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, + const std::vector<LayerSettings>& layers, + ANativeWindowBuffer* const buffer, + base::unique_fd* drawFence) { + if (layers.empty()) { + ALOGV("Drawing empty layer stack"); + return NO_ERROR; + } + + BindNativeBufferAsFramebuffer fbo(*this, buffer); + + if (fbo.getStatus() != NO_ERROR) { + ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", + buffer->handle); + checkErrors(); + return fbo.getStatus(); + } + + setViewportAndProjection(display.physicalDisplay, display.clip); + + setOutputDataSpace(display.outputDataspace); + setDisplayMaxLuminance(display.maxLuminance); + + mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform; + + Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2); + for (auto layer : layers) { + // for now, assume that all pixel sources are solid colors. + // TODO(alecmouri): support buffer sources + if (layer.source.buffer.buffer != nullptr) { + continue; + } + + setColorTransform(display.colorTransform * layer.colorTransform); + + mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; + + FloatRect bounds = layer.geometry.boundaries; + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + position[0] = vec2(bounds.left, bounds.top); + position[1] = vec2(bounds.left, bounds.bottom); + position[2] = vec2(bounds.right, bounds.bottom); + position[3] = vec2(bounds.right, bounds.top); + + half3 solidColor = layer.source.solidColor; + half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha); + setupLayerBlending(/*premultipliedAlpha=*/true, /*opaque=*/false, /*disableTexture=*/true, + color, /*cornerRadius=*/0.0); + setSourceDataSpace(layer.sourceDataspace); + + drawMesh(mesh); + } + + *drawFence = flush(); + // If flush failed or we don't support native fences, we need to force the + // gl command stream to be executed. + if (drawFence->get() < 0) { + bool success = finish(); + if (!success) { + ALOGE("Failed to flush RenderEngine commands"); + checkErrors(); + // Chances are, something illegal happened (either the caller passed + // us bad parameters, or we messed up our shader generation). + return INVALID_OPERATION; + } + } + + checkErrors(); + return NO_ERROR; +} + +void GLESRenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, + ui::Transform::orientation_flags rotation) { + setViewportAndProjection(Rect(vpw, vph), sourceCrop); + + if (rotation == ui::Transform::ROT_0) { + return; + } + + // Apply custom rotation to the projection. + float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f; + mat4 m = mState.projectionMatrix; + switch (rotation) { + case ui::Transform::ROT_90: + m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m; + break; + case ui::Transform::ROT_180: + m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m; + break; + case ui::Transform::ROT_270: + m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m; + break; + default: + break; + } + mState.projectionMatrix = m; +} + +void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) { + mVpWidth = viewport.getWidth(); + mVpHeight = viewport.getHeight(); + + // We pass the the top left corner instead of the bottom left corner, + // because since we're rendering off-screen first. + glViewport(viewport.left, viewport.top, mVpWidth, mVpHeight); + + mState.projectionMatrix = mat4::ortho(clip.left, clip.right, clip.top, clip.bottom, 0, 1); +} + +void GLESRenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, + const half4& color, float cornerRadius) { + mState.isPremultipliedAlpha = premultipliedAlpha; + mState.isOpaque = opaque; + mState.color = color; + mState.cornerRadius = cornerRadius; + + if (disableTexture) { + mState.textureEnabled = false; + } + + if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) { + glEnable(GL_BLEND); + glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } +} + +void GLESRenderEngine::setSourceY410BT2020(bool enable) { + mState.isY410BT2020 = enable; +} + +void GLESRenderEngine::setSourceDataSpace(Dataspace source) { + mDataSpace = source; +} + +void GLESRenderEngine::setOutputDataSpace(Dataspace dataspace) { + mOutputDataSpace = dataspace; +} + +void GLESRenderEngine::setDisplayMaxLuminance(const float maxLuminance) { + mState.displayMaxLuminance = maxLuminance; +} + +void GLESRenderEngine::setupLayerTexturing(const Texture& texture) { + GLuint target = texture.getTextureTarget(); + glBindTexture(target, texture.getTextureName()); + GLenum filter = GL_NEAREST; + if (texture.getFiltering()) { + filter = GL_LINEAR; + } + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); + + mState.texture = texture; + mState.textureEnabled = true; +} + +void GLESRenderEngine::setupLayerBlackedOut() { + glBindTexture(GL_TEXTURE_2D, mProtectedTexName); + Texture texture(Texture::TEXTURE_2D, mProtectedTexName); + texture.setDimensions(1, 1); // FIXME: we should get that from somewhere + mState.texture = texture; + mState.textureEnabled = true; +} + +void GLESRenderEngine::setColorTransform(const mat4& colorTransform) { + mState.colorMatrix = colorTransform; +} + +void GLESRenderEngine::disableTexturing() { + mState.textureEnabled = false; +} + +void GLESRenderEngine::disableBlending() { + glDisable(GL_BLEND); +} + +void GLESRenderEngine::setupFillWithColor(float r, float g, float b, float a) { + mState.isPremultipliedAlpha = true; + mState.isOpaque = false; + mState.color = half4(r, g, b, a); + mState.textureEnabled = false; + glDisable(GL_BLEND); +} + +void GLESRenderEngine::setupCornerRadiusCropSize(float width, float height) { + mState.cropSize = half2(width, height); +} + +void GLESRenderEngine::drawMesh(const Mesh& mesh) { + ATRACE_CALL(); + if (mesh.getTexCoordsSize()) { + glEnableVertexAttribArray(Program::texCoords); + glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE, + mesh.getByteStride(), mesh.getTexCoords()); + } + + glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, + mesh.getByteStride(), mesh.getPositions()); + + if (mState.cornerRadius > 0.0f) { + glEnableVertexAttribArray(Program::cropCoords); + glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, + mesh.getByteStride(), mesh.getCropCoords()); + } + + // By default, DISPLAY_P3 is the only supported wide color output. However, + // when HDR content is present, hardware composer may be able to handle + // BT2020 data space, in that case, the output data space is set to be + // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need + // to respect this and convert non-HDR content to HDR format. + if (mUseColorManagement) { + Description managedState = mState; + Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK); + Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); + Dataspace outputStandard = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::STANDARD_MASK); + Dataspace outputTransfer = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK); + bool needsXYZConversion = needsXYZTransformMatrix(); + + // NOTE: if the input standard of the input dataspace is not STANDARD_DCI_P3 or + // STANDARD_BT2020, it will be treated as STANDARD_BT709 + if (inputStandard != Dataspace::STANDARD_DCI_P3 && + inputStandard != Dataspace::STANDARD_BT2020) { + inputStandard = Dataspace::STANDARD_BT709; + } + + if (needsXYZConversion) { + // The supported input color spaces are standard RGB, Display P3 and BT2020. + switch (inputStandard) { + case Dataspace::STANDARD_DCI_P3: + managedState.inputTransformMatrix = mDisplayP3ToXyz; + break; + case Dataspace::STANDARD_BT2020: + managedState.inputTransformMatrix = mBt2020ToXyz; + break; + default: + managedState.inputTransformMatrix = mSrgbToXyz; + break; + } + + // The supported output color spaces are BT2020, Display P3 and standard RGB. + switch (outputStandard) { + case Dataspace::STANDARD_BT2020: + managedState.outputTransformMatrix = mXyzToBt2020; + break; + case Dataspace::STANDARD_DCI_P3: + managedState.outputTransformMatrix = mXyzToDisplayP3; + break; + default: + managedState.outputTransformMatrix = mXyzToSrgb; + break; + } + } else if (inputStandard != outputStandard) { + // At this point, the input data space and output data space could be both + // HDR data spaces, but they match each other, we do nothing in this case. + // In addition to the case above, the input data space could be + // - scRGB linear + // - scRGB non-linear + // - sRGB + // - Display P3 + // - BT2020 + // The output data spaces could be + // - sRGB + // - Display P3 + // - BT2020 + switch (outputStandard) { + case Dataspace::STANDARD_BT2020: + if (inputStandard == Dataspace::STANDARD_BT709) { + managedState.outputTransformMatrix = mSrgbToBt2020; + } else if (inputStandard == Dataspace::STANDARD_DCI_P3) { + managedState.outputTransformMatrix = mDisplayP3ToBt2020; + } + break; + case Dataspace::STANDARD_DCI_P3: + if (inputStandard == Dataspace::STANDARD_BT709) { + managedState.outputTransformMatrix = mSrgbToDisplayP3; + } else if (inputStandard == Dataspace::STANDARD_BT2020) { + managedState.outputTransformMatrix = mBt2020ToDisplayP3; + } + break; + default: + if (inputStandard == Dataspace::STANDARD_DCI_P3) { + managedState.outputTransformMatrix = mDisplayP3ToSrgb; + } else if (inputStandard == Dataspace::STANDARD_BT2020) { + managedState.outputTransformMatrix = mBt2020ToSrgb; + } + break; + } + } + + // we need to convert the RGB value to linear space and convert it back when: + // - there is a color matrix that is not an identity matrix, or + // - there is an output transform matrix that is not an identity matrix, or + // - the input transfer function doesn't match the output transfer function. + if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() || + inputTransfer != outputTransfer) { + managedState.inputTransferFunction = + Description::dataSpaceToTransferFunction(inputTransfer); + managedState.outputTransferFunction = + Description::dataSpaceToTransferFunction(outputTransfer); + } + + ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext + : mEGLContext, + managedState); + + glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); + + if (outputDebugPPMs) { + static uint64_t managedColorFrameCount = 0; + std::ostringstream out; + out << "/data/texture_out" << managedColorFrameCount++; + writePPM(out.str().c_str(), mVpWidth, mVpHeight); + } + } else { + ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext + : mEGLContext, + mState); + + glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); + } + + if (mesh.getTexCoordsSize()) { + glDisableVertexAttribArray(Program::texCoords); + } + + if (mState.cornerRadius > 0.0f) { + glDisableVertexAttribArray(Program::cropCoords); + } +} + +size_t GLESRenderEngine::getMaxTextureSize() const { + return mMaxTextureSize; +} + +size_t GLESRenderEngine::getMaxViewportDims() const { + return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1]; +} + +void GLESRenderEngine::dump(std::string& result) { + const GLExtensions& extensions = GLExtensions::getInstance(); + ProgramCache& cache = ProgramCache::getInstance(); + + StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion()); + StringAppendF(&result, "%s\n", extensions.getEGLExtensions()); + StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), + extensions.getVersion()); + StringAppendF(&result, "%s\n", extensions.getExtensions()); + StringAppendF(&result, "RenderEngine is in protected context : %d\n", mInProtectedContext); + StringAppendF(&result, "RenderEngine program cache size for unprotected context: %zu\n", + cache.getSize(mEGLContext)); + StringAppendF(&result, "RenderEngine program cache size for protected context: %zu\n", + cache.getSize(mProtectedEGLContext)); + StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n", + dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(), + dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str()); +} + +GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) { + int major, minor; + if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) { + if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) { + ALOGW("Unable to parse GL_VERSION string: \"%s\"", str); + return GLES_VERSION_1_0; + } + } + + if (major == 1 && minor == 0) return GLES_VERSION_1_0; + if (major == 1 && minor >= 1) return GLES_VERSION_1_1; + if (major == 2 && minor >= 0) return GLES_VERSION_2_0; + if (major == 3 && minor >= 0) return GLES_VERSION_3_0; + + ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor); + return GLES_VERSION_1_0; +} + +EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config, + EGLContext shareContext, bool useContextPriority, + Protection protection) { + EGLint renderableType = 0; + if (config == EGL_NO_CONFIG) { + renderableType = EGL_OPENGL_ES3_BIT; + } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) { + LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE"); + } + EGLint contextClientVersion = 0; + if (renderableType & EGL_OPENGL_ES3_BIT) { + contextClientVersion = 3; + } else if (renderableType & EGL_OPENGL_ES2_BIT) { + contextClientVersion = 2; + } else if (renderableType & EGL_OPENGL_ES_BIT) { + contextClientVersion = 1; + } else { + LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs"); + } + + std::vector<EGLint> contextAttributes; + contextAttributes.reserve(7); + contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION); + contextAttributes.push_back(contextClientVersion); + if (useContextPriority) { + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); + } + if (protection == Protection::PROTECTED) { + contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT); + contextAttributes.push_back(EGL_TRUE); + } + contextAttributes.push_back(EGL_NONE); + + EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data()); + + if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) { + // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus + // EGL_NO_CONTEXT so that we can abort. + if (config != EGL_NO_CONFIG) { + return context; + } + // If |config| is EGL_NO_CONFIG, we speculatively try to create GLES 3 context, so we should + // try to fall back to GLES 2. + contextAttributes[1] = 2; + context = eglCreateContext(display, config, shareContext, contextAttributes.data()); + } + + return context; +} + +EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config, + int hwcFormat, Protection protection) { + EGLConfig dummyConfig = config; + if (dummyConfig == EGL_NO_CONFIG) { + dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); + } + std::vector<EGLint> attributes; + attributes.reserve(7); + attributes.push_back(EGL_WIDTH); + attributes.push_back(1); + attributes.push_back(EGL_HEIGHT); + attributes.push_back(1); + if (protection == Protection::PROTECTED) { + attributes.push_back(EGL_PROTECTED_CONTENT_EXT); + attributes.push_back(EGL_TRUE); + } + attributes.push_back(EGL_NONE); + + return eglCreatePbufferSurface(display, dummyConfig, attributes.data()); +} + +bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const { + const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK); + const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK); + return standard == Dataspace::STANDARD_BT2020 && + (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG); +} + +// For convenience, we want to convert the input color space to XYZ color space first, +// and then convert from XYZ color space to output color space when +// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or +// HDR content will be tone-mapped to SDR; Or, +// - there are HDR PQ and HLG contents presented at the same time, where we want to convert +// HLG content to PQ content. +// In either case above, we need to operate the Y value in XYZ color space. Thus, when either +// input data space or output data space is HDR data space, and the input transfer function +// doesn't match the output transfer function, we would enable an intermediate transfrom to +// XYZ color space. +bool GLESRenderEngine::needsXYZTransformMatrix() const { + const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace); + const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace); + const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); + const Dataspace outputTransfer = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK); + + return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer; +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h new file mode 100644 index 0000000000..b6fff33061 --- /dev/null +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -0,0 +1,175 @@ +/* + * Copyright 2013 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. + */ + +#ifndef SF_GLESRENDERENGINE_H_ +#define SF_GLESRENDERENGINE_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <renderengine/RenderEngine.h> +#include <renderengine/private/Description.h> + +#define EGL_NO_CONFIG ((EGLConfig)0) + +namespace android { + +namespace renderengine { + +class Mesh; +class Texture; + +namespace gl { + +class GLImage; + +class GLESRenderEngine : public impl::RenderEngine { +public: + static std::unique_ptr<GLESRenderEngine> create(int hwcFormat, uint32_t featureFlags); + static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); + + GLESRenderEngine(uint32_t featureFlags, // See RenderEngine::FeatureFlag + EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy, + EGLContext protectedContext, EGLSurface protectedDummy); + ~GLESRenderEngine() override; + + std::unique_ptr<Framebuffer> createFramebuffer() override; + std::unique_ptr<Image> createImage() override; + + void primeCache() const override; + bool isCurrent() const override; + base::unique_fd flush() override; + bool finish() override; + bool waitFence(base::unique_fd fenceFd) override; + void clearWithColor(float red, float green, float blue, float alpha) override; + void fillRegionWithColor(const Region& region, float red, float green, float blue, + float alpha) override; + void setScissor(const Rect& region) override; + void disableScissor() override; + void genTextures(size_t count, uint32_t* names) override; + void deleteTextures(size_t count, uint32_t const* names) override; + void bindExternalTextureImage(uint32_t texName, const Image& image) override; + status_t bindFrameBuffer(Framebuffer* framebuffer) override; + void unbindFrameBuffer(Framebuffer* framebuffer) override; + void checkErrors() const override; + + bool isProtected() const override { return mInProtectedContext; } + bool supportsProtectedContent() const override; + bool useProtectedContext(bool useProtectedContext) override; + status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers, + ANativeWindowBuffer* buffer, base::unique_fd* drawFence) override; + + // internal to RenderEngine + EGLDisplay getEGLDisplay() const { return mEGLDisplay; } + EGLConfig getEGLConfig() const { return mEGLConfig; } + +protected: + void dump(std::string& result) override; + void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, + ui::Transform::orientation_flags rotation) override; + void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, + const half4& color, float cornerRadius) override; + void setupLayerTexturing(const Texture& texture) override; + void setupLayerBlackedOut() override; + void setupFillWithColor(float r, float g, float b, float a) override; + void setColorTransform(const mat4& colorTransform) override; + void disableTexturing() override; + void disableBlending() override; + void setupCornerRadiusCropSize(float width, float height) override; + + // HDR and color management related functions and state + void setSourceY410BT2020(bool enable) override; + void setSourceDataSpace(ui::Dataspace source) override; + void setOutputDataSpace(ui::Dataspace dataspace) override; + void setDisplayMaxLuminance(const float maxLuminance) override; + + // drawing + void drawMesh(const Mesh& mesh) override; + + size_t getMaxTextureSize() const override; + size_t getMaxViewportDims() const override; + +private: + enum GlesVersion { + GLES_VERSION_1_0 = 0x10000, + GLES_VERSION_1_1 = 0x10001, + GLES_VERSION_2_0 = 0x20000, + GLES_VERSION_3_0 = 0x30000, + }; + + static GlesVersion parseGlesVersion(const char* str); + static EGLContext createEglContext(EGLDisplay display, EGLConfig config, + EGLContext shareContext, bool useContextPriority, + Protection protection); + static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config, + int hwcFormat, Protection protection); + + // A data space is considered HDR data space if it has BT2020 color space + // with PQ or HLG transfer function. + bool isHdrDataSpace(const ui::Dataspace dataSpace) const; + bool needsXYZTransformMatrix() const; + // Defines the viewport, and sets the projection matrix to the projection + // defined by the clip. + void setViewportAndProjection(Rect viewport, Rect clip); + + EGLDisplay mEGLDisplay; + EGLConfig mEGLConfig; + EGLContext mEGLContext; + EGLSurface mDummySurface; + EGLContext mProtectedEGLContext; + EGLSurface mProtectedDummySurface; + GLuint mProtectedTexName; + GLint mMaxViewportDims[2]; + GLint mMaxTextureSize; + GLuint mVpWidth; + GLuint mVpHeight; + Description mState; + + mat4 mSrgbToXyz; + mat4 mDisplayP3ToXyz; + mat4 mBt2020ToXyz; + mat4 mXyzToSrgb; + mat4 mXyzToDisplayP3; + mat4 mXyzToBt2020; + mat4 mSrgbToDisplayP3; + mat4 mSrgbToBt2020; + mat4 mDisplayP3ToSrgb; + mat4 mDisplayP3ToBt2020; + mat4 mBt2020ToSrgb; + mat4 mBt2020ToDisplayP3; + + bool mInProtectedContext = false; + int32_t mFboHeight = 0; + + // Current dataspace of layer being rendered + ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN; + + // Current output dataspace of the render engine + ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN; + + // Whether device supports color management, currently color management + // supports sRGB, DisplayP3 color spaces. + const bool mUseColorManagement = false; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android + +#endif /* SF_GLESRENDERENGINE_H_ */ diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp new file mode 100644 index 0000000000..2924b0e8b3 --- /dev/null +++ b/libs/renderengine/gl/GLExtensions.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2010 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 "GLExtensions.h" + +#include <string> +#include <unordered_set> + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions) + +namespace android { +namespace renderengine { +namespace gl { + +namespace { + +class ExtensionSet { +public: + ExtensionSet(const char* extensions) { + char const* curr = extensions; + char const* head = curr; + do { + head = strchr(curr, ' '); + size_t len = head ? head - curr : strlen(curr); + if (len > 0) { + mExtensions.emplace(curr, len); + } + curr = head + 1; + } while (head); + } + + bool hasExtension(const char* extension) const { return mExtensions.count(extension) > 0; } + +private: + std::unordered_set<std::string> mExtensions; +}; + +} // anonymous namespace + +void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, + GLubyte const* version, GLubyte const* extensions) { + mVendor = (char const*)vendor; + mRenderer = (char const*)renderer; + mVersion = (char const*)version; + mExtensions = (char const*)extensions; + + ExtensionSet extensionSet(mExtensions.c_str()); + if (extensionSet.hasExtension("GL_EXT_protected_textures")) { + mHasProtectedTexture = true; + } +} + +char const* GLExtensions::getVendor() const { + return mVendor.string(); +} + +char const* GLExtensions::getRenderer() const { + return mRenderer.string(); +} + +char const* GLExtensions::getVersion() const { + return mVersion.string(); +} + +char const* GLExtensions::getExtensions() const { + return mExtensions.string(); +} + +void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) { + mEGLVersion = eglVersion; + mEGLExtensions = eglExtensions; + + ExtensionSet extensionSet(eglExtensions); + + // EGL_ANDROIDX_no_config_context is an experimental extension with no + // written specification. It will be replaced by something more formal. + // SurfaceFlinger is using it to allow a single EGLContext to render to + // both a 16-bit primary display framebuffer and a 32-bit virtual display + // framebuffer. + // + // EGL_KHR_no_config_context is official extension to allow creating a + // context that works with any surface of a display. + if (extensionSet.hasExtension("EGL_ANDROIDX_no_config_context") || + extensionSet.hasExtension("EGL_KHR_no_config_context")) { + mHasNoConfigContext = true; + } + + if (extensionSet.hasExtension("EGL_ANDROID_native_fence_sync")) { + mHasNativeFenceSync = true; + } + if (extensionSet.hasExtension("EGL_KHR_fence_sync")) { + mHasFenceSync = true; + } + if (extensionSet.hasExtension("EGL_KHR_wait_sync")) { + mHasWaitSync = true; + } + if (extensionSet.hasExtension("EGL_EXT_protected_content")) { + mHasProtectedContent = true; + } + if (extensionSet.hasExtension("EGL_IMG_context_priority")) { + mHasContextPriority = true; + } + if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) { + mHasSurfacelessContext = true; + } +} + +char const* GLExtensions::getEGLVersion() const { + return mEGLVersion.string(); +} + +char const* GLExtensions::getEGLExtensions() const { + return mEGLExtensions.string(); +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h new file mode 100644 index 0000000000..ef000090a8 --- /dev/null +++ b/libs/renderengine/gl/GLExtensions.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef ANDROID_SF_GLEXTENSION_H +#define ANDROID_SF_GLEXTENSION_H + +#include <stdint.h> +#include <sys/types.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <utils/Singleton.h> +#include <utils/String8.h> + +namespace android { +namespace renderengine { +namespace gl { + +class GLExtensions : public Singleton<GLExtensions> { +public: + bool hasNoConfigContext() const { return mHasNoConfigContext; } + bool hasNativeFenceSync() const { return mHasNativeFenceSync; } + bool hasFenceSync() const { return mHasFenceSync; } + bool hasWaitSync() const { return mHasWaitSync; } + bool hasProtectedContent() const { return mHasProtectedContent; } + bool hasContextPriority() const { return mHasContextPriority; } + bool hasSurfacelessContext() const { return mHasSurfacelessContext; } + bool hasProtectedTexture() const { return mHasProtectedTexture; } + + void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version, + GLubyte const* extensions); + char const* getVendor() const; + char const* getRenderer() const; + char const* getVersion() const; + char const* getExtensions() const; + + void initWithEGLStrings(char const* eglVersion, char const* eglExtensions); + char const* getEGLVersion() const; + char const* getEGLExtensions() const; + +protected: + GLExtensions() = default; + +private: + friend class Singleton<GLExtensions>; + + bool mHasNoConfigContext = false; + bool mHasNativeFenceSync = false; + bool mHasFenceSync = false; + bool mHasWaitSync = false; + bool mHasProtectedContent = false; + bool mHasContextPriority = false; + bool mHasSurfacelessContext = false; + bool mHasProtectedTexture = false; + + String8 mVendor; + String8 mRenderer; + String8 mVersion; + String8 mExtensions; + String8 mEGLVersion; + String8 mEGLExtensions; + + GLExtensions(const GLExtensions&); + GLExtensions& operator=(const GLExtensions&); +}; + +} // namespace gl +} // namespace renderengine +} // namespace android + +#endif // ANDROID_SF_GLEXTENSION_H diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp new file mode 100644 index 0000000000..4a519bb422 --- /dev/null +++ b/libs/renderengine/gl/GLFramebuffer.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GLFramebuffer.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <nativebase/nativebase.h> +#include "GLESRenderEngine.h" + +namespace android { +namespace renderengine { +namespace gl { + +GLFramebuffer::GLFramebuffer(const GLESRenderEngine& engine) + : mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) { + glGenTextures(1, &mTextureName); + glGenFramebuffers(1, &mFramebufferName); +} + +GLFramebuffer::~GLFramebuffer() { + glDeleteFramebuffers(1, &mFramebufferName); + glDeleteTextures(1, &mTextureName); + eglDestroyImageKHR(mEGLDisplay, mEGLImage); +} + +bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) { + if (mEGLImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mEGLDisplay, mEGLImage); + mEGLImage = EGL_NO_IMAGE_KHR; + mBufferWidth = 0; + mBufferHeight = 0; + } + + if (nativeBuffer) { + EGLint attributes[] = { + isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, + isProtected ? EGL_TRUE : EGL_NONE, + EGL_NONE, + }; + mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + nativeBuffer, attributes); + if (mEGLImage == EGL_NO_IMAGE_KHR) { + return false; + } + mBufferWidth = nativeBuffer->width; + mBufferHeight = nativeBuffer->height; + } + return true; +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h new file mode 100644 index 0000000000..5043c590a9 --- /dev/null +++ b/libs/renderengine/gl/GLFramebuffer.h @@ -0,0 +1,56 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <renderengine/Framebuffer.h> + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { +namespace gl { + +class GLESRenderEngine; + +class GLFramebuffer : public renderengine::Framebuffer { +public: + explicit GLFramebuffer(const GLESRenderEngine& engine); + ~GLFramebuffer() override; + + bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) override; + EGLImageKHR getEGLImage() const { return mEGLImage; } + uint32_t getTextureName() const { return mTextureName; } + uint32_t getFramebufferName() const { return mFramebufferName; } + int32_t getBufferHeight() const { return mBufferHeight; } + int32_t getBufferWidth() const { return mBufferWidth; } + +private: + EGLDisplay mEGLDisplay; + EGLImageKHR mEGLImage; + uint32_t mTextureName, mFramebufferName; + + int32_t mBufferHeight = 0; + int32_t mBufferWidth = 0; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp new file mode 100644 index 0000000000..587cb313c2 --- /dev/null +++ b/libs/renderengine/gl/GLImage.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GLImage.h" + +#include <vector> + +#include <log/log.h> +#include "GLESRenderEngine.h" +#include "GLExtensions.h" + +namespace android { +namespace renderengine { +namespace gl { + +static std::vector<EGLint> buildAttributeList(bool isProtected) { + std::vector<EGLint> attrs; + attrs.reserve(16); + + attrs.push_back(EGL_IMAGE_PRESERVED_KHR); + attrs.push_back(EGL_TRUE); + + if (isProtected && GLExtensions::getInstance().hasProtectedContent()) { + attrs.push_back(EGL_PROTECTED_CONTENT_EXT); + attrs.push_back(EGL_TRUE); + } + + attrs.push_back(EGL_NONE); + + return attrs; +} + +GLImage::GLImage(const GLESRenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {} + +GLImage::~GLImage() { + setNativeWindowBuffer(nullptr, false); +} + +bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) { + if (mEGLImage != EGL_NO_IMAGE_KHR) { + if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) { + ALOGE("failed to destroy image: %#x", eglGetError()); + } + mEGLImage = EGL_NO_IMAGE_KHR; + } + + if (buffer) { + std::vector<EGLint> attrs = buildAttributeList(isProtected); + mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + static_cast<EGLClientBuffer>(buffer), attrs.data()); + if (mEGLImage == EGL_NO_IMAGE_KHR) { + ALOGE("failed to create EGLImage: %#x", eglGetError()); + return false; + } + mProtected = isProtected; + } + + return true; +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/GLImage.h b/libs/renderengine/gl/GLImage.h new file mode 100644 index 0000000000..59d6ce3549 --- /dev/null +++ b/libs/renderengine/gl/GLImage.h @@ -0,0 +1,54 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <android-base/macros.h> +#include <renderengine/Image.h> + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { +namespace gl { + +class GLESRenderEngine; + +class GLImage : public renderengine::Image { +public: + explicit GLImage(const GLESRenderEngine& engine); + ~GLImage() override; + + bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override; + + EGLImageKHR getEGLImage() const { return mEGLImage; } + bool isProtected() const { return mProtected; } + +private: + EGLDisplay mEGLDisplay; + EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR; + bool mProtected = false; + + DISALLOW_COPY_AND_ASSIGN(GLImage); +}; + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp new file mode 100644 index 0000000000..fe9d909923 --- /dev/null +++ b/libs/renderengine/gl/Program.cpp @@ -0,0 +1,153 @@ +/*Gluint + * Copyright 2013 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 "Program.h" + +#include <stdint.h> + +#include <log/log.h> +#include <math/mat4.h> +#include <utils/String8.h> +#include "ProgramCache.h" + +namespace android { +namespace renderengine { +namespace gl { + +Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment) + : mInitialized(false) { + GLuint vertexId = buildShader(vertex, GL_VERTEX_SHADER); + GLuint fragmentId = buildShader(fragment, GL_FRAGMENT_SHADER); + GLuint programId = glCreateProgram(); + glAttachShader(programId, vertexId); + glAttachShader(programId, fragmentId); + glBindAttribLocation(programId, position, "position"); + glBindAttribLocation(programId, texCoords, "texCoords"); + glBindAttribLocation(programId, cropCoords, "cropCoords"); + glLinkProgram(programId); + + GLint status; + glGetProgramiv(programId, GL_LINK_STATUS, &status); + if (status != GL_TRUE) { + ALOGE("Error while linking shaders:"); + GLint infoLen = 0; + glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen > 1) { + GLchar log[infoLen]; + glGetProgramInfoLog(programId, infoLen, 0, &log[0]); + ALOGE("%s", log); + } + glDetachShader(programId, vertexId); + glDetachShader(programId, fragmentId); + glDeleteShader(vertexId); + glDeleteShader(fragmentId); + glDeleteProgram(programId); + } else { + mProgram = programId; + mVertexShader = vertexId; + mFragmentShader = fragmentId; + mInitialized = true; + mProjectionMatrixLoc = glGetUniformLocation(programId, "projection"); + mTextureMatrixLoc = glGetUniformLocation(programId, "texture"); + mSamplerLoc = glGetUniformLocation(programId, "sampler"); + mColorLoc = glGetUniformLocation(programId, "color"); + mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance"); + mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix"); + mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix"); + mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius"); + mCropCenterLoc = glGetUniformLocation(programId, "cropCenter"); + + // set-up the default values for our uniforms + glUseProgram(programId); + glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, mat4().asArray()); + glEnableVertexAttribArray(0); + } +} + +bool Program::isValid() const { + return mInitialized; +} + +void Program::use() { + glUseProgram(mProgram); +} + +GLuint Program::getAttrib(const char* name) const { + // TODO: maybe use a local cache + return glGetAttribLocation(mProgram, name); +} + +GLint Program::getUniform(const char* name) const { + // TODO: maybe use a local cache + return glGetUniformLocation(mProgram, name); +} + +GLuint Program::buildShader(const char* source, GLenum type) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, 0); + glCompileShader(shader); + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) { + // Some drivers return wrong values for GL_INFO_LOG_LENGTH + // use a fixed size instead + GLchar log[512]; + glGetShaderInfoLog(shader, sizeof(log), 0, log); + ALOGE("Error while compiling shader: \n%s\n%s", source, log); + glDeleteShader(shader); + return 0; + } + return shader; +} + +void Program::setUniforms(const Description& desc) { + // TODO: we should have a mechanism here to not always reset uniforms that + // didn't change for this program. + + if (mSamplerLoc >= 0) { + glUniform1i(mSamplerLoc, 0); + glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.texture.getMatrix().asArray()); + } + if (mColorLoc >= 0) { + const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a}; + glUniform4fv(mColorLoc, 1, color); + } + if (mInputTransformMatrixLoc >= 0) { + mat4 inputTransformMatrix = desc.inputTransformMatrix; + glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray()); + } + if (mOutputTransformMatrixLoc >= 0) { + // The output transform matrix and color matrix can be combined as one matrix + // that is applied right before applying OETF. + mat4 outputTransformMatrix = desc.colorMatrix * desc.outputTransformMatrix; + glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, outputTransformMatrix.asArray()); + } + if (mDisplayMaxLuminanceLoc >= 0) { + glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance); + } + if (mCornerRadiusLoc >= 0) { + glUniform1f(mCornerRadiusLoc, desc.cornerRadius); + } + if (mCropCenterLoc >= 0) { + glUniform2f(mCropCenterLoc, desc.cropSize.x / 2.0f, desc.cropSize.y / 2.0f); + } + // these uniforms are always present + glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray()); +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h new file mode 100644 index 0000000000..bc9cf08b8b --- /dev/null +++ b/libs/renderengine/gl/Program.h @@ -0,0 +1,109 @@ +/* + * Copyright 2013 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. + */ + +#ifndef SF_RENDER_ENGINE_PROGRAM_H +#define SF_RENDER_ENGINE_PROGRAM_H + +#include <stdint.h> + +#include <GLES2/gl2.h> +#include <renderengine/private/Description.h> +#include "ProgramCache.h" + +namespace android { + +class String8; + +namespace renderengine { +namespace gl { + +/* + * Abstracts a GLSL program comprising a vertex and fragment shader + */ +class Program { +public: + // known locations for position and texture coordinates + enum { + /* position of each vertex for vertex shader */ + position = 0, + + /* UV coordinates for texture mapping */ + texCoords = 1, + + /* Crop coordinates, in pixels */ + cropCoords = 2 + }; + + Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment); + ~Program() = default; + + /* whether this object is usable */ + bool isValid() const; + + /* Binds this program to the GLES context */ + void use(); + + /* Returns the location of the specified attribute */ + GLuint getAttrib(const char* name) const; + + /* Returns the location of the specified uniform */ + GLint getUniform(const char* name) const; + + /* set-up uniforms from the description */ + void setUniforms(const Description& desc); + +private: + GLuint buildShader(const char* source, GLenum type); + + // whether the initialization succeeded + bool mInitialized; + + // Name of the OpenGL program and shaders + GLuint mProgram; + GLuint mVertexShader; + GLuint mFragmentShader; + + /* location of the projection matrix uniform */ + GLint mProjectionMatrixLoc; + + /* location of the texture matrix uniform */ + GLint mTextureMatrixLoc; + + /* location of the sampler uniform */ + GLint mSamplerLoc; + + /* location of the color uniform */ + GLint mColorLoc; + + /* location of display luminance uniform */ + GLint mDisplayMaxLuminanceLoc; + + /* location of transform matrix */ + GLint mInputTransformMatrixLoc; + GLint mOutputTransformMatrixLoc; + + /* location of corner radius uniform */ + GLint mCornerRadiusLoc; + + /* location of surface crop origin uniform, for rounded corner clipping */ + GLint mCropCenterLoc; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android + +#endif /* SF_RENDER_ENGINE_PROGRAM_H */ diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp new file mode 100644 index 0000000000..49bdd2a319 --- /dev/null +++ b/libs/renderengine/gl/ProgramCache.cpp @@ -0,0 +1,727 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "ProgramCache.h" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <log/log.h> +#include <renderengine/private/Description.h> +#include <utils/String8.h> +#include <utils/Trace.h> +#include "Program.h" + +ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::ProgramCache) + +namespace android { +namespace renderengine { +namespace gl { + +/* + * A simple formatter class to automatically add the endl and + * manage the indentation. + */ + +class Formatter; +static Formatter& indent(Formatter& f); +static Formatter& dedent(Formatter& f); + +class Formatter { + String8 mString; + int mIndent; + typedef Formatter& (*FormaterManipFunc)(Formatter&); + friend Formatter& indent(Formatter& f); + friend Formatter& dedent(Formatter& f); + +public: + Formatter() : mIndent(0) {} + + String8 getString() const { return mString; } + + friend Formatter& operator<<(Formatter& out, const char* in) { + for (int i = 0; i < out.mIndent; i++) { + out.mString.append(" "); + } + out.mString.append(in); + out.mString.append("\n"); + return out; + } + friend inline Formatter& operator<<(Formatter& out, const String8& in) { + return operator<<(out, in.string()); + } + friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) { + return (*func)(to); + } +}; +Formatter& indent(Formatter& f) { + f.mIndent++; + return f; +} +Formatter& dedent(Formatter& f) { + f.mIndent--; + return f; +} + +void ProgramCache::primeCache(EGLContext context, bool useColorManagement) { + auto& cache = mCaches[context]; + uint32_t shaderCount = 0; + uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK + | Key::ROUNDED_CORNERS_MASK; + // Prime the cache for all combinations of the above masks, + // leaving off the experimental color matrix mask options. + + nsecs_t timeBefore = systemTime(); + for (uint32_t keyVal = 0; keyVal <= keyMask; keyVal++) { + Key shaderKey; + shaderKey.set(keyMask, keyVal); + uint32_t tex = shaderKey.getTextureTarget(); + if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) { + continue; + } + if (cache.count(shaderKey) == 0) { + cache.emplace(shaderKey, generateProgram(shaderKey)); + shaderCount++; + } + } + + // Prime for sRGB->P3 conversion + if (useColorManagement) { + Key shaderKey; + shaderKey.set(Key::BLEND_MASK | Key::TEXTURE_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK | + Key::INPUT_TF_MASK | Key::OUTPUT_TF_MASK, + Key::BLEND_PREMULT | Key::TEXTURE_EXT | Key::OUTPUT_TRANSFORM_MATRIX_ON | + Key::INPUT_TF_SRGB | Key::OUTPUT_TF_SRGB); + for (int i = 0; i < 4; i++) { + shaderKey.set(Key::OPACITY_MASK, + (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT); + shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE); + if (cache.count(shaderKey) == 0) { + cache.emplace(shaderKey, generateProgram(shaderKey)); + shaderCount++; + } + } + } + + nsecs_t timeAfter = systemTime(); + float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; + ALOGD("shader cache generated - %u shaders in %f ms\n", shaderCount, compileTimeMs); +} + +ProgramCache::Key ProgramCache::computeKey(const Description& description) { + Key needs; + needs.set(Key::TEXTURE_MASK, + !description.textureEnabled + ? Key::TEXTURE_OFF + : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES + ? Key::TEXTURE_EXT + : description.texture.getTextureTarget() == GL_TEXTURE_2D + ? Key::TEXTURE_2D + : Key::TEXTURE_OFF) + .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE) + .set(Key::BLEND_MASK, + description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL) + .set(Key::OPACITY_MASK, + description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT) + .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK, + description.hasInputTransformMatrix() + ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF) + .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK, + description.hasOutputTransformMatrix() || description.hasColorMatrix() + ? Key::OUTPUT_TRANSFORM_MATRIX_ON + : Key::OUTPUT_TRANSFORM_MATRIX_OFF) + .set(Key::ROUNDED_CORNERS_MASK, + description.cornerRadius > 0 + ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF); + + needs.set(Key::Y410_BT2020_MASK, + description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF); + + if (needs.hasTransformMatrix() || + (description.inputTransferFunction != description.outputTransferFunction)) { + switch (description.inputTransferFunction) { + case Description::TransferFunction::LINEAR: + default: + needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR); + break; + case Description::TransferFunction::SRGB: + needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_SRGB); + break; + case Description::TransferFunction::ST2084: + needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_ST2084); + break; + case Description::TransferFunction::HLG: + needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_HLG); + break; + } + + switch (description.outputTransferFunction) { + case Description::TransferFunction::LINEAR: + default: + needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR); + break; + case Description::TransferFunction::SRGB: + needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_SRGB); + break; + case Description::TransferFunction::ST2084: + needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_ST2084); + break; + case Description::TransferFunction::HLG: + needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_HLG); + break; + } + } + + return needs; +} + +// Generate EOTF that converts signal values to relative display light, +// both normalized to [0, 1]. +void ProgramCache::generateEOTF(Formatter& fs, const Key& needs) { + switch (needs.getInputTF()) { + case Key::INPUT_TF_SRGB: + fs << R"__SHADER__( + float EOTF_sRGB(float srgb) { + return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4); + } + + vec3 EOTF_sRGB(const vec3 srgb) { + return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b)); + } + + vec3 EOTF(const vec3 srgb) { + return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb)); + } + )__SHADER__"; + break; + case Key::INPUT_TF_ST2084: + fs << R"__SHADER__( + vec3 EOTF(const highp vec3 color) { + const highp float m1 = (2610.0 / 4096.0) / 4.0; + const highp float m2 = (2523.0 / 4096.0) * 128.0; + const highp float c1 = (3424.0 / 4096.0); + const highp float c2 = (2413.0 / 4096.0) * 32.0; + const highp float c3 = (2392.0 / 4096.0) * 32.0; + + highp vec3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / vec3(m2)); + tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp); + return pow(tmp, 1.0 / vec3(m1)); + } + )__SHADER__"; + break; + case Key::INPUT_TF_HLG: + fs << R"__SHADER__( + highp float EOTF_channel(const highp float channel) { + const highp float a = 0.17883277; + const highp float b = 0.28466892; + const highp float c = 0.55991073; + return channel <= 0.5 ? channel * channel / 3.0 : + (exp((channel - c) / a) + b) / 12.0; + } + + vec3 EOTF(const highp vec3 color) { + return vec3(EOTF_channel(color.r), EOTF_channel(color.g), + EOTF_channel(color.b)); + } + )__SHADER__"; + break; + default: + fs << R"__SHADER__( + vec3 EOTF(const vec3 linear) { + return linear; + } + )__SHADER__"; + break; + } +} + +void ProgramCache::generateToneMappingProcess(Formatter& fs, const Key& needs) { + // Convert relative light to absolute light. + switch (needs.getInputTF()) { + case Key::INPUT_TF_ST2084: + fs << R"__SHADER__( + highp vec3 ScaleLuminance(highp vec3 color) { + return color * 10000.0; + } + )__SHADER__"; + break; + case Key::INPUT_TF_HLG: + fs << R"__SHADER__( + highp vec3 ScaleLuminance(highp vec3 color) { + // The formula is: + // alpha * pow(Y, gamma - 1.0) * color + beta; + // where alpha is 1000.0, gamma is 1.2, beta is 0.0. + return color * 1000.0 * pow(color.y, 0.2); + } + )__SHADER__"; + break; + default: + fs << R"__SHADER__( + highp vec3 ScaleLuminance(highp vec3 color) { + return color * displayMaxLuminance; + } + )__SHADER__"; + break; + } + + // Tone map absolute light to display luminance range. + switch (needs.getInputTF()) { + case Key::INPUT_TF_ST2084: + case Key::INPUT_TF_HLG: + switch (needs.getOutputTF()) { + case Key::OUTPUT_TF_HLG: + // Right now when mixed PQ and HLG contents are presented, + // HLG content will always be converted to PQ. However, for + // completeness, we simply clamp the value to [0.0, 1000.0]. + fs << R"__SHADER__( + highp vec3 ToneMap(highp vec3 color) { + return clamp(color, 0.0, 1000.0); + } + )__SHADER__"; + break; + case Key::OUTPUT_TF_ST2084: + fs << R"__SHADER__( + highp vec3 ToneMap(highp vec3 color) { + return color; + } + )__SHADER__"; + break; + default: + fs << R"__SHADER__( + highp vec3 ToneMap(highp vec3 color) { + const float maxMasteringLumi = 1000.0; + const float maxContentLumi = 1000.0; + const float maxInLumi = min(maxMasteringLumi, maxContentLumi); + float maxOutLumi = displayMaxLuminance; + + float nits = color.y; + + // clamp to max input luminance + nits = clamp(nits, 0.0, maxInLumi); + + // scale [0.0, maxInLumi] to [0.0, maxOutLumi] + if (maxInLumi <= maxOutLumi) { + return color * (maxOutLumi / maxInLumi); + } else { + // three control points + const float x0 = 10.0; + const float y0 = 17.0; + float x1 = maxOutLumi * 0.75; + float y1 = x1; + float x2 = x1 + (maxInLumi - x1) / 2.0; + float y2 = y1 + (maxOutLumi - y1) * 0.75; + + // horizontal distances between the last three control points + float h12 = x2 - x1; + float h23 = maxInLumi - x2; + // tangents at the last three control points + float m1 = (y2 - y1) / h12; + float m3 = (maxOutLumi - y2) / h23; + float m2 = (m1 + m3) / 2.0; + + if (nits < x0) { + // scale [0.0, x0] to [0.0, y0] linearly + float slope = y0 / x0; + return color * slope; + } else if (nits < x1) { + // scale [x0, x1] to [y0, y1] linearly + float slope = (y1 - y0) / (x1 - x0); + nits = y0 + (nits - x0) * slope; + } else if (nits < x2) { + // scale [x1, x2] to [y1, y2] using Hermite interp + float t = (nits - x1) / h12; + nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) + + (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t; + } else { + // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp + float t = (nits - x2) / h23; + nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) + + (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t; + } + } + + // color.y is greater than x0 and is thus non-zero + return color * (nits / color.y); + } + )__SHADER__"; + break; + } + break; + default: + // inverse tone map; the output luminance can be up to maxOutLumi. + fs << R"__SHADER__( + highp vec3 ToneMap(highp vec3 color) { + const float maxOutLumi = 3000.0; + + const float x0 = 5.0; + const float y0 = 2.5; + float x1 = displayMaxLuminance * 0.7; + float y1 = maxOutLumi * 0.15; + float x2 = displayMaxLuminance * 0.9; + float y2 = maxOutLumi * 0.45; + float x3 = displayMaxLuminance; + float y3 = maxOutLumi; + + float c1 = y1 / 3.0; + float c2 = y2 / 2.0; + float c3 = y3 / 1.5; + + float nits = color.y; + + float scale; + if (nits <= x0) { + // scale [0.0, x0] to [0.0, y0] linearly + const float slope = y0 / x0; + return color * slope; + } else if (nits <= x1) { + // scale [x0, x1] to [y0, y1] using a curve + float t = (nits - x0) / (x1 - x0); + nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1; + } else if (nits <= x2) { + // scale [x1, x2] to [y1, y2] using a curve + float t = (nits - x1) / (x2 - x1); + nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2; + } else { + // scale [x2, x3] to [y2, y3] using a curve + float t = (nits - x2) / (x3 - x2); + nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3; + } + + // color.y is greater than x0 and is thus non-zero + return color * (nits / color.y); + } + )__SHADER__"; + break; + } + + // convert absolute light to relative light. + switch (needs.getOutputTF()) { + case Key::OUTPUT_TF_ST2084: + fs << R"__SHADER__( + highp vec3 NormalizeLuminance(highp vec3 color) { + return color / 10000.0; + } + )__SHADER__"; + break; + case Key::OUTPUT_TF_HLG: + fs << R"__SHADER__( + highp vec3 NormalizeLuminance(highp vec3 color) { + return color / 1000.0 * pow(color.y / 1000.0, -0.2 / 1.2); + } + )__SHADER__"; + break; + default: + fs << R"__SHADER__( + highp vec3 NormalizeLuminance(highp vec3 color) { + return color / displayMaxLuminance; + } + )__SHADER__"; + break; + } +} + +// Generate OOTF that modifies the relative scence light to relative display light. +void ProgramCache::generateOOTF(Formatter& fs, const ProgramCache::Key& needs) { + if (!needs.needsToneMapping()) { + fs << R"__SHADER__( + highp vec3 OOTF(const highp vec3 color) { + return color; + } + )__SHADER__"; + } else { + generateToneMappingProcess(fs, needs); + fs << R"__SHADER__( + highp vec3 OOTF(const highp vec3 color) { + return NormalizeLuminance(ToneMap(ScaleLuminance(color))); + } + )__SHADER__"; + } +} + +// Generate OETF that converts relative display light to signal values, +// both normalized to [0, 1] +void ProgramCache::generateOETF(Formatter& fs, const Key& needs) { + switch (needs.getOutputTF()) { + case Key::OUTPUT_TF_SRGB: + fs << R"__SHADER__( + float OETF_sRGB(const float linear) { + return linear <= 0.0031308 ? + linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055; + } + + vec3 OETF_sRGB(const vec3 linear) { + return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); + } + + vec3 OETF(const vec3 linear) { + return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)); + } + )__SHADER__"; + break; + case Key::OUTPUT_TF_ST2084: + fs << R"__SHADER__( + vec3 OETF(const vec3 linear) { + const highp float m1 = (2610.0 / 4096.0) / 4.0; + const highp float m2 = (2523.0 / 4096.0) * 128.0; + const highp float c1 = (3424.0 / 4096.0); + const highp float c2 = (2413.0 / 4096.0) * 32.0; + const highp float c3 = (2392.0 / 4096.0) * 32.0; + + highp vec3 tmp = pow(linear, vec3(m1)); + tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); + return pow(tmp, vec3(m2)); + } + )__SHADER__"; + break; + case Key::OUTPUT_TF_HLG: + fs << R"__SHADER__( + highp float OETF_channel(const highp float channel) { + const highp float a = 0.17883277; + const highp float b = 0.28466892; + const highp float c = 0.55991073; + return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) : + a * log(12.0 * channel - b) + c; + } + + vec3 OETF(const highp vec3 color) { + return vec3(OETF_channel(color.r), OETF_channel(color.g), + OETF_channel(color.b)); + } + )__SHADER__"; + break; + default: + fs << R"__SHADER__( + vec3 OETF(const vec3 linear) { + return linear; + } + )__SHADER__"; + break; + } +} + +String8 ProgramCache::generateVertexShader(const Key& needs) { + Formatter vs; + if (needs.isTexturing()) { + vs << "attribute vec4 texCoords;" + << "varying vec2 outTexCoords;"; + } + if (needs.hasRoundedCorners()) { + vs << "attribute lowp vec4 cropCoords;"; + vs << "varying lowp vec2 outCropCoords;"; + } + vs << "attribute vec4 position;" + << "uniform mat4 projection;" + << "uniform mat4 texture;" + << "void main(void) {" << indent << "gl_Position = projection * position;"; + if (needs.isTexturing()) { + vs << "outTexCoords = (texture * texCoords).st;"; + } + if (needs.hasRoundedCorners()) { + vs << "outCropCoords = cropCoords.st;"; + } + vs << dedent << "}"; + return vs.getString(); +} + +String8 ProgramCache::generateFragmentShader(const Key& needs) { + Formatter fs; + if (needs.getTextureTarget() == Key::TEXTURE_EXT) { + fs << "#extension GL_OES_EGL_image_external : require"; + } + + // default precision is required-ish in fragment shaders + fs << "precision mediump float;"; + + if (needs.getTextureTarget() == Key::TEXTURE_EXT) { + fs << "uniform samplerExternalOES sampler;" + << "varying vec2 outTexCoords;"; + } else if (needs.getTextureTarget() == Key::TEXTURE_2D) { + fs << "uniform sampler2D sampler;" + << "varying vec2 outTexCoords;"; + } + + if (needs.hasRoundedCorners()) { + // Rounded corners implementation using a signed distance function. + fs << R"__SHADER__( + uniform float cornerRadius; + uniform vec2 cropCenter; + varying vec2 outCropCoords; + + /** + * This function takes the current crop coordinates and calculates an alpha value based + * on the corner radius and distance from the crop center. + */ + float applyCornerRadius(vec2 cropCoords) + { + vec2 position = cropCoords - cropCenter; + vec2 dist = abs(position) + vec2(cornerRadius) - cropCenter; + float plane = length(max(dist, vec2(0.0))); + return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0); + } + )__SHADER__"; + } + + if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) { + fs << "uniform vec4 color;"; + } + + if (needs.isY410BT2020()) { + fs << R"__SHADER__( + vec3 convertY410BT2020(const vec3 color) { + const vec3 offset = vec3(0.0625, 0.5, 0.5); + const mat3 transform = mat3( + vec3(1.1678, 1.1678, 1.1678), + vec3( 0.0, -0.1878, 2.1481), + vec3(1.6836, -0.6523, 0.0)); + // Y is in G, U is in R, and V is in B + return clamp(transform * (color.grb - offset), 0.0, 1.0); + } + )__SHADER__"; + } + + if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) { + // Currently, display maximum luminance is needed when doing tone mapping. + if (needs.needsToneMapping()) { + fs << "uniform float displayMaxLuminance;"; + } + + if (needs.hasInputTransformMatrix()) { + fs << "uniform mat4 inputTransformMatrix;"; + fs << R"__SHADER__( + highp vec3 InputTransform(const highp vec3 color) { + return clamp(vec3(inputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0); + } + )__SHADER__"; + } else { + fs << R"__SHADER__( + highp vec3 InputTransform(const highp vec3 color) { + return color; + } + )__SHADER__"; + } + + // the transformation from a wider colorspace to a narrower one can + // result in >1.0 or <0.0 pixel values + if (needs.hasOutputTransformMatrix()) { + fs << "uniform mat4 outputTransformMatrix;"; + fs << R"__SHADER__( + highp vec3 OutputTransform(const highp vec3 color) { + return clamp(vec3(outputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0); + } + )__SHADER__"; + } else { + fs << R"__SHADER__( + highp vec3 OutputTransform(const highp vec3 color) { + return clamp(color, 0.0, 1.0); + } + )__SHADER__"; + } + + generateEOTF(fs, needs); + generateOOTF(fs, needs); + generateOETF(fs, needs); + } + + fs << "void main(void) {" << indent; + if (needs.isTexturing()) { + fs << "gl_FragColor = texture2D(sampler, outTexCoords);"; + if (needs.isY410BT2020()) { + fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);"; + } + } else { + fs << "gl_FragColor.rgb = color.rgb;"; + fs << "gl_FragColor.a = 1.0;"; + } + if (needs.isOpaque()) { + fs << "gl_FragColor.a = 1.0;"; + } + if (needs.hasAlpha()) { + // modulate the current alpha value with alpha set + if (needs.isPremultiplied()) { + // ... and the color too if we're premultiplied + fs << "gl_FragColor *= color.a;"; + } else { + fs << "gl_FragColor.a *= color.a;"; + } + } + + if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) { + if (!needs.isOpaque() && needs.isPremultiplied()) { + // un-premultiply if needed before linearization + // avoid divide by 0 by adding 0.5/256 to the alpha channel + fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);"; + } + fs << "gl_FragColor.rgb = " + "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));"; + if (!needs.isOpaque() && needs.isPremultiplied()) { + // and re-premultiply if needed after gamma correction + fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);"; + } + } + + if (needs.hasRoundedCorners()) { + if (needs.isPremultiplied()) { + fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));"; + } else { + fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);"; + } + } + + fs << dedent << "}"; + return fs.getString(); +} + +std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) { + ATRACE_CALL(); + + // vertex shader + String8 vs = generateVertexShader(needs); + + // fragment shader + String8 fs = generateFragmentShader(needs); + + return std::make_unique<Program>(needs, vs.string(), fs.string()); +} + +void ProgramCache::useProgram(EGLContext context, const Description& description) { + // generate the key for the shader based on the description + Key needs(computeKey(description)); + + // look-up the program in the cache + auto& cache = mCaches[context]; + auto it = cache.find(needs); + if (it == cache.end()) { + // we didn't find our program, so generate one... + nsecs_t time = systemTime(); + it = cache.emplace(needs, generateProgram(needs)).first; + time = systemTime() - time; + + ALOGV(">>> generated new program for context %p: needs=%08X, time=%u ms (%zu programs)", + context, needs.mKey, uint32_t(ns2ms(time)), cache.size()); + } + + // here we have a suitable program for this description + std::unique_ptr<Program>& program = it->second; + if (program->isValid()) { + program->use(); + program->setUniforms(description); + } +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h new file mode 100644 index 0000000000..400ad74fca --- /dev/null +++ b/libs/renderengine/gl/ProgramCache.h @@ -0,0 +1,221 @@ +/* + * Copyright 2013 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. + */ + +#ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H +#define SF_RENDER_ENGINE_PROGRAMCACHE_H + +#include <memory> +#include <unordered_map> + +#include <EGL/egl.h> +#include <GLES2/gl2.h> +#include <renderengine/private/Description.h> +#include <utils/Singleton.h> +#include <utils/TypeHelpers.h> + +namespace android { + +class String8; + +namespace renderengine { + +struct Description; + +namespace gl { + +class Formatter; +class Program; + +/* + * This class generates GLSL programs suitable to handle a given + * Description. It's responsible for figuring out what to + * generate from a Description. + * It also maintains a cache of these Programs. + */ +class ProgramCache : public Singleton<ProgramCache> { +public: + /* + * Key is used to retrieve a Program in the cache. + * A Key is generated from a Description. + */ + class Key { + friend class ProgramCache; + typedef uint32_t key_t; + key_t mKey; + + public: + enum { + BLEND_SHIFT = 0, + BLEND_MASK = 1 << BLEND_SHIFT, + BLEND_PREMULT = 1 << BLEND_SHIFT, + BLEND_NORMAL = 0 << BLEND_SHIFT, + + OPACITY_SHIFT = 1, + OPACITY_MASK = 1 << OPACITY_SHIFT, + OPACITY_OPAQUE = 1 << OPACITY_SHIFT, + OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT, + + ALPHA_SHIFT = 2, + ALPHA_MASK = 1 << ALPHA_SHIFT, + ALPHA_LT_ONE = 1 << ALPHA_SHIFT, + ALPHA_EQ_ONE = 0 << ALPHA_SHIFT, + + TEXTURE_SHIFT = 3, + TEXTURE_MASK = 3 << TEXTURE_SHIFT, + TEXTURE_OFF = 0 << TEXTURE_SHIFT, + TEXTURE_EXT = 1 << TEXTURE_SHIFT, + TEXTURE_2D = 2 << TEXTURE_SHIFT, + + ROUNDED_CORNERS_SHIFT = 5, + ROUNDED_CORNERS_MASK = 1 << ROUNDED_CORNERS_SHIFT, + ROUNDED_CORNERS_OFF = 0 << ROUNDED_CORNERS_SHIFT, + ROUNDED_CORNERS_ON = 1 << ROUNDED_CORNERS_SHIFT, + + INPUT_TRANSFORM_MATRIX_SHIFT = 6, + INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT, + INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT, + INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT, + + OUTPUT_TRANSFORM_MATRIX_SHIFT = 7, + OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT, + OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT, + OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT, + + INPUT_TF_SHIFT = 8, + INPUT_TF_MASK = 3 << INPUT_TF_SHIFT, + INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT, + INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT, + INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT, + INPUT_TF_HLG = 3 << INPUT_TF_SHIFT, + + OUTPUT_TF_SHIFT = 10, + OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT, + OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT, + OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT, + OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT, + OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT, + + Y410_BT2020_SHIFT = 12, + Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT, + Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT, + Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT, + }; + + inline Key() : mKey(0) {} + inline Key(const Key& rhs) : mKey(rhs.mKey) {} + + inline Key& set(key_t mask, key_t value) { + mKey = (mKey & ~mask) | value; + return *this; + } + + inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; } + inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); } + inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; } + inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; } + inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; } + inline bool hasRoundedCorners() const { + return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON; + } + inline bool hasInputTransformMatrix() const { + return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON; + } + inline bool hasOutputTransformMatrix() const { + return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON; + } + inline bool hasTransformMatrix() const { + return hasInputTransformMatrix() || hasOutputTransformMatrix(); + } + inline int getInputTF() const { return (mKey & INPUT_TF_MASK); } + inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); } + + // When HDR and non-HDR contents are mixed, or different types of HDR contents are + // mixed, we will do a tone mapping process to tone map the input content to output + // content. Currently, the following conversions handled, they are: + // * SDR -> HLG + // * SDR -> PQ + // * HLG -> PQ + inline bool needsToneMapping() const { + int inputTF = getInputTF(); + int outputTF = getOutputTF(); + + // Return false when converting from SDR to SDR. + if (inputTF == Key::INPUT_TF_SRGB && outputTF == Key::OUTPUT_TF_LINEAR) { + return false; + } + if (inputTF == Key::INPUT_TF_LINEAR && outputTF == Key::OUTPUT_TF_SRGB) { + return false; + } + + inputTF >>= Key::INPUT_TF_SHIFT; + outputTF >>= Key::OUTPUT_TF_SHIFT; + return inputTF != outputTF; + } + inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; } + + // for use by std::unordered_map + + bool operator==(const Key& other) const { return mKey == other.mKey; } + + struct Hash { + size_t operator()(const Key& key) const { return static_cast<size_t>(key.mKey); } + }; + }; + + ProgramCache() = default; + ~ProgramCache() = default; + + // Generate shaders to populate the cache + void primeCache(const EGLContext context, bool useColorManagement); + + size_t getSize(const EGLContext context) { return mCaches[context].size(); } + + // useProgram lookup a suitable program in the cache or generates one + // if none can be found. + void useProgram(const EGLContext context, const Description& description); + +private: + // compute a cache Key from a Description + static Key computeKey(const Description& description); + // Generate EOTF based from Key. + static void generateEOTF(Formatter& fs, const Key& needs); + // Generate necessary tone mapping methods for OOTF. + static void generateToneMappingProcess(Formatter& fs, const Key& needs); + // Generate OOTF based from Key. + static void generateOOTF(Formatter& fs, const Key& needs); + // Generate OETF based from Key. + static void generateOETF(Formatter& fs, const Key& needs); + // generates a program from the Key + static std::unique_ptr<Program> generateProgram(const Key& needs); + // generates the vertex shader from the Key + static String8 generateVertexShader(const Key& needs); + // generates the fragment shader from the Key + static String8 generateFragmentShader(const Key& needs); + + // Key/Value map used for caching Programs. Currently the cache + // is never shrunk (and the GL program objects are never deleted). + std::unordered_map<EGLContext, std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash>> + mCaches; +}; + +} // namespace gl +} // namespace renderengine + +ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key) + +} // namespace android + +#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */ diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h new file mode 100644 index 0000000000..0c923535bb --- /dev/null +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -0,0 +1,61 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/mat4.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> + +namespace android { +namespace renderengine { + +// DisplaySettings contains the settings that are applicable when drawing all +// layers for a given display. +struct DisplaySettings { + // Rectangle describing the physical display. We will project from the + // logical clip onto this rectangle. + Rect physicalDisplay = Rect::INVALID_RECT; + + // Rectangle bounded by the x,y- clipping planes in the logical display, so + // that the orthographic projection matrix can be computed. When + // constructing this matrix, z-coordinate bound are assumed to be at z=0 and + // z=1. + Rect clip = Rect::INVALID_RECT; + + // Global transform to apply to all layers. + mat4 globalTransform = mat4(); + + // Maximum luminance pulled from the display's HDR capabilities. + float maxLuminance = 1.0f; + + // Output dataspace that will be populated if wide color gamut is used, or + // DataSpace::UNKNOWN otherwise. + ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN; + + // Additional color transform to apply in linear space after transforming + // to the output dataspace. + mat4 colorTransform = mat4(); + + // Region that will be cleared to (0, 0, 0, 0) prior to rendering. + // clearRegion will first be transformed by globalTransform so that it will + // be in the same coordinate space as the rendered layers. + Region clearRegion = Region::INVALID_REGION; +}; + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/libs/renderengine/include/renderengine/Framebuffer.h new file mode 100644 index 0000000000..66eb9ef206 --- /dev/null +++ b/libs/renderengine/include/renderengine/Framebuffer.h @@ -0,0 +1,34 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { + +class Framebuffer { +public: + virtual ~Framebuffer() = default; + + virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) = 0; +}; + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/include/renderengine/Image.h b/libs/renderengine/include/renderengine/Image.h new file mode 100644 index 0000000000..3bb47318ef --- /dev/null +++ b/libs/renderengine/include/renderengine/Image.h @@ -0,0 +1,31 @@ +/* + * Copyright 2017 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 + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { + +class Image { +public: + virtual ~Image() = default; + virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0; +}; + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h new file mode 100644 index 0000000000..93abf5c458 --- /dev/null +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -0,0 +1,96 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/mat4.h> +#include <math/vec3.h> +#include <renderengine/Texture.h> +#include <ui/FloatRect.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <ui/Transform.h> + +namespace android { +namespace renderengine { + +// Metadata describing the input buffer to render from. +struct Buffer { + // Buffer containing the image that we will render. + // If buffer == nullptr, then the rest of the fields in this struct will be + // ignored. + sp<GraphicBuffer> buffer = nullptr; + + // Texture identifier to bind the external texture to. + // TODO(alecmouri): This is GL-specific...make the type backend-agnostic. + uint32_t textureName = 0; + + // Whether to use filtering when rendering the texture. + bool useTextureFiltering = false; + + // Transform matrix to apply to texture coordinates. + mat4 textureTransform = mat4(); + + // Wheteher to use pre-multiplied alpha + bool usePremultipliedAlpha = true; + + // HDR color-space setting for Y410. + bool isY410BT2020 = false; +}; + +// Metadata describing the layer geometry. +struct Geometry { + // Boundaries of the layer. + FloatRect boundaries = FloatRect(); + + // Transform matrix to apply to mesh coordinates. + mat4 positionTransform = mat4(); +}; + +// Descriptor of the source pixels for this layer. +struct PixelSource { + // Source buffer + Buffer buffer = Buffer(); + + // The solid color with which to fill the layer. + // This should only be populated if we don't render from an application + // buffer. + half3 solidColor = half3(0.0f, 0.0f, 0.0f); +}; + +// The settings that RenderEngine requires for correctly rendering a Layer. +struct LayerSettings { + // Geometry information + Geometry geometry = Geometry(); + + // Source pixels for this layer. + PixelSource source = PixelSource(); + + // Alpha option to apply to the source pixels + half alpha = half(0.0); + + // Color space describing how the source pixels should be interpreted. + ui::Dataspace sourceDataspace; + + // Additional layer-specific color transform to be applied before the global + // transform. + mat4 colorTransform; +}; + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h new file mode 100644 index 0000000000..7618424e85 --- /dev/null +++ b/libs/renderengine/include/renderengine/Mesh.h @@ -0,0 +1,115 @@ +/* + * Copyright 2013 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. + */ + +#ifndef SF_RENDER_ENGINE_MESH_H +#define SF_RENDER_ENGINE_MESH_H + +#include <vector> + +#include <stdint.h> + +namespace android { +namespace renderengine { + +class Mesh { +public: + enum Primitive { + TRIANGLES = 0x0004, // GL_TRIANGLES + TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP + TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN + }; + + Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0); + ~Mesh() = default; + + /* + * VertexArray handles the stride automatically. + */ + template <typename TYPE> + class VertexArray { + friend class Mesh; + float* mData; + size_t mStride; + VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {} + + public: + TYPE& operator[](size_t index) { return *reinterpret_cast<TYPE*>(&mData[index * mStride]); } + TYPE const& operator[](size_t index) const { + return *reinterpret_cast<TYPE const*>(&mData[index * mStride]); + } + }; + + template <typename TYPE> + VertexArray<TYPE> getPositionArray() { + return VertexArray<TYPE>(getPositions(), mStride); + } + + template <typename TYPE> + VertexArray<TYPE> getTexCoordArray() { + return VertexArray<TYPE>(getTexCoords(), mStride); + } + + template <typename TYPE> + VertexArray<TYPE> getCropCoordArray() { + return VertexArray<TYPE>(getCropCoords(), mStride); + } + + Primitive getPrimitive() const; + + // returns a pointer to the vertices positions + float const* getPositions() const; + + // returns a pointer to the vertices texture coordinates + float const* getTexCoords() const; + + // returns a pointer to the vertices crop coordinates + float const* getCropCoords() const; + + // number of vertices in this mesh + size_t getVertexCount() const; + + // dimension of vertices + size_t getVertexSize() const; + + // dimension of texture coordinates + size_t getTexCoordsSize() const; + + // return stride in bytes + size_t getByteStride() const; + + // return stride in floats + size_t getStride() const; + +private: + Mesh(const Mesh&); + Mesh& operator=(const Mesh&); + Mesh const& operator=(const Mesh&) const; + + float* getPositions(); + float* getTexCoords(); + float* getCropCoords(); + + std::vector<float> mVertices; + size_t mVertexCount; + size_t mVertexSize; + size_t mTexCoordsSize; + size_t mStride; + Primitive mPrimitive; +}; + +} // namespace renderengine +} // namespace android +#endif /* SF_RENDER_ENGINE_MESH_H */ diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h new file mode 100644 index 0000000000..20dd996ec2 --- /dev/null +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -0,0 +1,228 @@ +/* + * Copyright 2013 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. + */ + +#ifndef SF_RENDERENGINE_H_ +#define SF_RENDERENGINE_H_ + +#include <stdint.h> +#include <sys/types.h> +#include <memory> + +#include <android-base/unique_fd.h> +#include <math/mat4.h> +#include <renderengine/DisplaySettings.h> +#include <renderengine/Framebuffer.h> +#include <renderengine/Image.h> +#include <renderengine/LayerSettings.h> +#include <ui/GraphicTypes.h> +#include <ui/Transform.h> + +/** + * Allows to set RenderEngine backend to GLES (default) or Vulkan (NOT yet supported). + */ +#define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend" + +struct ANativeWindowBuffer; + +namespace android { + +class Rect; +class Region; + +namespace renderengine { + +class BindNativeBufferAsFramebuffer; +class Image; +class Mesh; +class Texture; + +namespace impl { +class RenderEngine; +} + +enum class Protection { + UNPROTECTED = 1, + PROTECTED = 2, +}; + +class RenderEngine { +public: + enum FeatureFlag { + USE_COLOR_MANAGEMENT = 1 << 0, // Device manages color + USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context + }; + + static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags); + + virtual ~RenderEngine() = 0; + + // ----- BEGIN DEPRECATED INTERFACE ----- + // This interface, while still in use until a suitable replacement is built, + // should be considered deprecated, minus some methods which still may be + // used to support legacy behavior. + + virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0; + virtual std::unique_ptr<Image> createImage() = 0; + + virtual void primeCache() const = 0; + + // dump the extension strings. always call the base class. + virtual void dump(std::string& result) = 0; + + virtual bool useNativeFenceSync() const = 0; + virtual bool useWaitSync() const = 0; + + virtual bool isCurrent() const = 0; + + // helpers + // flush submits RenderEngine command stream for execution and returns a + // native fence fd that is signaled when the execution has completed. It + // returns -1 on errors. + virtual base::unique_fd flush() = 0; + // finish waits until RenderEngine command stream has been executed. It + // returns false on errors. + virtual bool finish() = 0; + // waitFence inserts a wait on an external fence fd to RenderEngine + // command stream. It returns false on errors. + virtual bool waitFence(base::unique_fd fenceFd) = 0; + + virtual void clearWithColor(float red, float green, float blue, float alpha) = 0; + virtual void fillRegionWithColor(const Region& region, float red, float green, float blue, + float alpha) = 0; + + virtual void setScissor(const Rect& region) = 0; + virtual void disableScissor() = 0; + virtual void genTextures(size_t count, uint32_t* names) = 0; + virtual void deleteTextures(size_t count, uint32_t const* names) = 0; + virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0; + // When binding a native buffer, it must be done before setViewportAndProjection + // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation. + virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0; + virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0; + + // set-up + virtual void checkErrors() const = 0; + virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, + ui::Transform::orientation_flags rotation) = 0; + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, + const half4& color, float cornerRadius) = 0; + virtual void setupLayerTexturing(const Texture& texture) = 0; + virtual void setupLayerBlackedOut() = 0; + virtual void setupFillWithColor(float r, float g, float b, float a) = 0; + // Sets up the crop size for corner radius clipping. + // + // Having corner radius will force GPU composition on the layer and its children, drawing it + // with a special shader. The shader will receive the radius and the crop rectangle as input, + // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1. + // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop + // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be + // in local layer coordinate space, so we have to take the layer transform into account when + // walking up the tree. + virtual void setupCornerRadiusCropSize(float width, float height) = 0; + + // Set a color transform matrix that is applied in linear space right before OETF. + virtual void setColorTransform(const mat4& /* colorTransform */) = 0; + virtual void disableTexturing() = 0; + virtual void disableBlending() = 0; + + // HDR and color management support + virtual void setSourceY410BT2020(bool enable) = 0; + virtual void setSourceDataSpace(ui::Dataspace source) = 0; + virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0; + virtual void setDisplayMaxLuminance(const float maxLuminance) = 0; + + // drawing + virtual void drawMesh(const Mesh& mesh) = 0; + + // queries + virtual size_t getMaxTextureSize() const = 0; + virtual size_t getMaxViewportDims() const = 0; + + // ----- END DEPRECATED INTERFACE ----- + + // ----- BEGIN NEW INTERFACE ----- + + virtual bool isProtected() const = 0; + virtual bool supportsProtectedContent() const = 0; + virtual bool useProtectedContext(bool useProtectedContext) = 0; + + // Renders layers for a particular display via GPU composition. This method + // should be called for every display that needs to be rendered via the GPU. + // @param display The display-wide settings that should be applied prior to + // drawing any layers. + // @param layers The layers to draw onto the display, in Z-order. + // @param buffer The buffer which will be drawn to. This buffer will be + // ready once displayFence fires. + // @param drawFence A pointer to a fence, which will fire when the buffer + // has been drawn to and is ready to be examined. The fence will be + // initialized by this method. The caller will be responsible for owning the + // fence. + // @return An error code indicating whether drawing was successful. For + // now, this always returns NO_ERROR. + // TODO(alecmouri): Consider making this a multi-display API, so that the + // caller deoes not need to handle multiple fences. + virtual status_t drawLayers(const DisplaySettings& display, + const std::vector<LayerSettings>& layers, + ANativeWindowBuffer* buffer, base::unique_fd* drawFence) = 0; + + // TODO(alecmouri): Expose something like bindTexImage() so that devices + // that don't support native sync fences can get rid of code duplicated + // between BufferStateLayer and BufferQueueLayer for binding an external + // texture. + + // TODO(alecmouri): Add API to help with managing a texture pool. +}; + +class BindNativeBufferAsFramebuffer { +public: + BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer) + : mEngine(engine), mFramebuffer(mEngine.createFramebuffer()), mStatus(NO_ERROR) { + mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected()) + ? mEngine.bindFrameBuffer(mFramebuffer.get()) + : NO_MEMORY; + } + ~BindNativeBufferAsFramebuffer() { + mFramebuffer->setNativeWindowBuffer(nullptr, false); + mEngine.unbindFrameBuffer(mFramebuffer.get()); + } + status_t getStatus() const { return mStatus; } + +private: + RenderEngine& mEngine; + std::unique_ptr<Framebuffer> mFramebuffer; + status_t mStatus; +}; + +namespace impl { + +// impl::RenderEngine contains common implementation that is graphics back-end agnostic. +class RenderEngine : public renderengine::RenderEngine { +public: + virtual ~RenderEngine() = 0; + + bool useNativeFenceSync() const override; + bool useWaitSync() const override; + +protected: + RenderEngine(uint32_t featureFlags); + const uint32_t mFeatureFlags; +}; + +} // namespace impl +} // namespace renderengine +} // namespace android + +#endif /* SF_RENDERENGINE_H_ */ diff --git a/libs/renderengine/include/renderengine/Texture.h b/libs/renderengine/include/renderengine/Texture.h new file mode 100644 index 0000000000..c69ace0603 --- /dev/null +++ b/libs/renderengine/include/renderengine/Texture.h @@ -0,0 +1,60 @@ +/* + * Copyright 2013 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. + */ + +#ifndef SF_RENDER_ENGINE_TEXTURE_H +#define SF_RENDER_ENGINE_TEXTURE_H + +#include <stdint.h> + +#include <math/mat4.h> + +namespace android { +namespace renderengine { + +class Texture { +public: + enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 }; + + Texture(); + Texture(Target textureTarget, uint32_t textureName); + ~Texture(); + + void init(Target textureTarget, uint32_t textureName); + + void setMatrix(float const* matrix); + void setFiltering(bool enabled); + void setDimensions(size_t width, size_t height); + + uint32_t getTextureName() const; + uint32_t getTextureTarget() const; + + const mat4& getMatrix() const; + bool getFiltering() const; + size_t getWidth() const; + size_t getHeight() const; + +private: + uint32_t mTextureName; + uint32_t mTextureTarget; + size_t mWidth; + size_t mHeight; + bool mFiltering; + mat4 mTextureMatrix; +}; + +} // namespace renderengine +} // namespace android +#endif /* SF_RENDER_ENGINE_TEXTURE_H */ diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h new file mode 100644 index 0000000000..bd2055f4e5 --- /dev/null +++ b/libs/renderengine/include/renderengine/private/Description.h @@ -0,0 +1,87 @@ +/* + * Copyright 2013 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. + */ + +#ifndef SF_RENDER_ENGINE_DESCRIPTION_H_ +#define SF_RENDER_ENGINE_DESCRIPTION_H_ + +#include <renderengine/Texture.h> +#include <ui/GraphicTypes.h> + +namespace android { +namespace renderengine { + +/* + * This is the structure that holds the state of the rendering engine. + * This class is used to generate a corresponding GLSL program and set the + * appropriate uniform. + */ +struct Description { + enum class TransferFunction : int { + LINEAR, + SRGB, + ST2084, + HLG, // Hybrid Log-Gamma for HDR. + }; + + static TransferFunction dataSpaceToTransferFunction(ui::Dataspace dataSpace); + + Description() = default; + ~Description() = default; + + bool hasInputTransformMatrix() const; + bool hasOutputTransformMatrix() const; + bool hasColorMatrix() const; + + // whether textures are premultiplied + bool isPremultipliedAlpha = false; + // whether this layer is marked as opaque + bool isOpaque = true; + + // corner radius of the layer + float cornerRadius = 0; + + // Size of the rounded rectangle we are cropping to + half2 cropSize; + + // Texture this layer uses + Texture texture; + bool textureEnabled = false; + + // color used when texturing is disabled or when setting alpha. + half4 color; + + // true if the sampled pixel values are in Y410/BT2020 rather than RGBA + bool isY410BT2020 = false; + + // transfer functions for the input/output + TransferFunction inputTransferFunction = TransferFunction::LINEAR; + TransferFunction outputTransferFunction = TransferFunction::LINEAR; + + float displayMaxLuminance; + + // projection matrix + mat4 projectionMatrix; + + // The color matrix will be applied in linear space right before OETF. + mat4 colorMatrix; + mat4 inputTransformMatrix; + mat4 outputTransformMatrix; +}; + +} // namespace renderengine +} // namespace android + +#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */ diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp new file mode 100644 index 0000000000..9b483ef51d --- /dev/null +++ b/libs/renderengine/tests/Android.bp @@ -0,0 +1,38 @@ +// Copyright 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "librenderengine_test", + defaults: ["surfaceflinger_defaults"], + test_suites: ["device-tests"], + srcs: [ + "RenderEngineTest.cpp", + ], + static_libs: [ + "libgmock", + "librenderengine", + ], + shared_libs: [ + "libbase", + "libcutils", + "libEGL", + "libGLESv2", + "libgui", + "liblog", + "libnativewindow", + "libsync", + "libui", + "libutils", + ], +} diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp new file mode 100644 index 0000000000..a0542dd0cd --- /dev/null +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -0,0 +1,446 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <renderengine/RenderEngine.h> +#include <sync/sync.h> +#include <ui/PixelFormat.h> + +constexpr int DEFAULT_DISPLAY_WIDTH = 128; +constexpr int DEFAULT_DISPLAY_HEIGHT = 256; +constexpr int DEFAULT_DISPLAY_OFFSET = 64; + +namespace android { + +struct RenderEngineTest : public ::testing::Test { + sp<GraphicBuffer> allocateDefaultBuffer() { + return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, + HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + "output"); + } + + RenderEngineTest() { mBuffer = allocateDefaultBuffer(); } + + void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a, + uint8_t tolerance = 0) { + uint8_t* pixels; + mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + + auto colorCompare = [tolerance](uint8_t a, uint8_t b) { + uint8_t tmp = a >= b ? a - b : b - a; + return tmp <= tolerance; + }; + int32_t maxFails = 10; + int32_t fails = 0; + for (int32_t j = 0; j < region.getHeight(); j++) { + const uint8_t* src = + pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4; + for (int32_t i = 0; i < region.getWidth(); i++) { + const uint8_t expected[4] = {r, g, b, a}; + bool equal = std::equal(src, src + 4, expected, colorCompare); + EXPECT_TRUE(equal) + << "pixel @ (" << region.left + i << ", " << region.top + j << "): " + << "expected (" << static_cast<uint32_t>(r) << ", " + << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", " + << static_cast<uint32_t>(a) << "), " + << "got (" << static_cast<uint32_t>(src[0]) << ", " + << static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2]) + << ", " << static_cast<uint32_t>(src[3]) << ")"; + src += 4; + if (!equal && ++fails >= maxFails) { + break; + } + } + if (fails >= maxFails) { + break; + } + } + mBuffer->unlock(); + } + + static Rect fullscreenRect() { return Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); } + + static Rect offsetRect() { + return Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT); + } + + static Rect offsetRectAtZero() { + return Rect(DEFAULT_DISPLAY_WIDTH - DEFAULT_DISPLAY_OFFSET, + DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET); + } + + static void invokeDraw(renderengine::DisplaySettings settings, + std::vector<renderengine::LayerSettings> layers, + sp<GraphicBuffer> buffer) { + base::unique_fd fence; + status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), &fence); + + int fd = fence.release(); + if (fd >= 0) { + sync_wait(fd, -1); + close(fd); + } + + ASSERT_EQ(NO_ERROR, status); + } + + static void drawEmptyLayers() { + renderengine::DisplaySettings settings; + std::vector<renderengine::LayerSettings> layers; + // Meaningless buffer since we don't do any drawing + sp<GraphicBuffer> buffer = new GraphicBuffer(); + invokeDraw(settings, layers, buffer); + } + + template <typename SourceVariant> + void fillBuffer(half r, half g, half b, half a); + + template <typename SourceVariant> + void fillRedBuffer(); + + template <typename SourceVariant> + void fillGreenBuffer(); + + template <typename SourceVariant> + void fillBlueBuffer(); + + template <typename SourceVariant> + void fillRedTransparentBuffer(); + + template <typename SourceVariant> + void fillRedOffsetBuffer(); + + template <typename SourceVariant> + void fillBufferPhysicalOffset(); + + template <typename SourceVariant> + void fillBufferCheckers(mat4 transform); + + template <typename SourceVariant> + void fillBufferCheckersRotate0(); + + template <typename SourceVariant> + void fillBufferCheckersRotate90(); + + template <typename SourceVariant> + void fillBufferCheckersRotate180(); + + template <typename SourceVariant> + void fillBufferCheckersRotate270(); + + template <typename SourceVariant> + void fillBufferLayerTransform(); + + template <typename SourceVariant> + void fillBufferColorTransform(); + + // Dumb hack to get aroud the fact that tear-down for renderengine isn't + // well defined right now, so we can't create multiple instances + static std::unique_ptr<renderengine::RenderEngine> sRE; + + sp<GraphicBuffer> mBuffer; +}; + +std::unique_ptr<renderengine::RenderEngine> RenderEngineTest::sRE = + renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888), 0); + +struct ColorSourceVariant { + static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b) { + layer.source.solidColor = half3(r, g, b); + } +}; + +template <typename SourceVariant> +void RenderEngineTest::fillBuffer(half r, half g, half b, half a) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.geometry.boundaries = fullscreenRect().toFloatRect(); + SourceVariant::fillColor(layer, r, g, b); + layer.alpha = a; + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); +} + +template <typename SourceVariant> +void RenderEngineTest::fillRedBuffer() { + fillBuffer<SourceVariant>(1.0f, 0.0f, 0.0f, 1.0f); + expectBufferColor(fullscreenRect(), 255, 0, 0, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillGreenBuffer() { + fillBuffer<SourceVariant>(0.0f, 1.0f, 0.0f, 1.0f); + expectBufferColor(fullscreenRect(), 0, 255, 0, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBlueBuffer() { + fillBuffer<SourceVariant>(0.0f, 0.0f, 1.0f, 1.0f); + expectBufferColor(fullscreenRect(), 0, 0, 255, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillRedTransparentBuffer() { + fillBuffer<SourceVariant>(1.0f, 0.0f, 0.0f, .2f); + expectBufferColor(fullscreenRect(), 51, 0, 0, 51); +} + +template <typename SourceVariant> +void RenderEngineTest::fillRedOffsetBuffer() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = offsetRect(); + settings.clip = offsetRectAtZero(); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.geometry.boundaries = offsetRectAtZero().toFloatRect(); + SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f); + layer.alpha = 1.0f; + + layers.push_back(layer); + invokeDraw(settings, layers, mBuffer); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferPhysicalOffset() { + fillRedOffsetBuffer<SourceVariant>(); + + expectBufferColor(Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT), + 255, 0, 0, 255); + Rect offsetRegionLeft(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_HEIGHT); + Rect offsetRegionTop(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_OFFSET); + + expectBufferColor(offsetRegionLeft, 0, 0, 0, 0); + expectBufferColor(offsetRegionTop, 0, 0, 0, 0); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferCheckers(mat4 transform) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + // Here logical space is 2x2 + settings.clip = Rect(2, 2); + settings.globalTransform = transform; + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layerOne; + Rect rectOne(0, 0, 1, 1); + layerOne.geometry.boundaries = rectOne.toFloatRect(); + SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f); + layerOne.alpha = 1.0f; + + renderengine::LayerSettings layerTwo; + Rect rectTwo(0, 1, 1, 2); + layerTwo.geometry.boundaries = rectTwo.toFloatRect(); + SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f); + layerTwo.alpha = 1.0f; + + renderengine::LayerSettings layerThree; + Rect rectThree(1, 0, 2, 1); + layerThree.geometry.boundaries = rectThree.toFloatRect(); + SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f); + layerThree.alpha = 1.0f; + + layers.push_back(layerOne); + layers.push_back(layerTwo); + layers.push_back(layerThree); + + invokeDraw(settings, layers, mBuffer); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferCheckersRotate0() { + fillBufferCheckers<SourceVariant>(mat4()); + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, + 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT / 2), + 0, 0, 255, 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 0, 0, 0, 0); + expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, + DEFAULT_DISPLAY_HEIGHT), + 0, 255, 0, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferCheckersRotate90() { + mat4 matrix = mat4(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1); + fillBufferCheckers<SourceVariant>(matrix); + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 255, 0, + 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT / 2), + 255, 0, 0, 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 0, 0, 255, 255); + expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, + DEFAULT_DISPLAY_HEIGHT), + 0, 0, 0, 0); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferCheckersRotate180() { + mat4 matrix = mat4(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 2, 2, 0, 1); + fillBufferCheckers<SourceVariant>(matrix); + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0, + 0); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT / 2), + 0, 255, 0, 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 255, 0, 0, 255); + expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, + DEFAULT_DISPLAY_HEIGHT), + 0, 0, 255, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferCheckersRotate270() { + mat4 matrix = mat4(0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1); + fillBufferCheckers<SourceVariant>(matrix); + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 255, + 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT / 2), + 0, 0, 0, 0); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 0, 255, 0, 255); + expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, + DEFAULT_DISPLAY_HEIGHT), + 255, 0, 0, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferLayerTransform() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + // Here logical space is 2x2 + settings.clip = Rect(2, 2); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + // Translate one pixel diagonally + layer.geometry.positionTransform = mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1); + layer.source.solidColor = half3(1.0f, 0.0f, 0.0f); + layer.alpha = 1.0f; + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); + + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0, 0); + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 255, 0, 0, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferColorTransform() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = Rect(1, 1); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + layer.source.solidColor = half3(0.5f, 0.25f, 0.125f); + layer.alpha = 1.0f; + + // construct a fake color matrix + // annihilate green and blue channels + settings.colorTransform = mat4::scale(vec4(1, 0, 0, 1)); + // set red channel to red + green + layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); + + expectBufferColor(fullscreenRect(), 191, 0, 0, 255); +} + +TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) { + drawEmptyLayers(); +} + +TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { + fillRedBuffer<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { + fillGreenBuffer<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { + fillBlueBuffer<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { + fillRedTransparentBuffer<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { + fillBufferPhysicalOffset<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { + fillBufferCheckersRotate0<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { + fillBufferCheckersRotate90<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { + fillBufferCheckersRotate180<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { + fillBufferCheckersRotate270<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { + fillBufferLayerTransform<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { + fillBufferLayerTransform<ColorSourceVariant>(); +} + +} // namespace android diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index a0e368c7e4..d9a986ed00 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -318,7 +318,7 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi // If the sensor is protected by a permission we need to know if it is // a runtime one to determine whether we can use the permission cache. sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); - if (binder != 0) { + if (binder != nullptr) { sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder); mRequiredPermissionRuntime = permCtrl->isRuntimePermission( String16(mRequiredPermission)); diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp index 6f68fb5f7b..46ba7c6250 100644 --- a/libs/sensor/SensorEventQueue.cpp +++ b/libs/sensor/SensorEventQueue.cpp @@ -37,7 +37,7 @@ namespace android { // ---------------------------------------------------------------------------- SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection) - : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0), + : mSensorEventConnection(connection), mRecBuffer(nullptr), mAvailable(0), mConsumed(0), mNumAcksToSend(0) { mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT]; } @@ -82,9 +82,9 @@ ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) { sp<Looper> SensorEventQueue::getLooper() const { Mutex::Autolock _l(mLock); - if (mLooper == 0) { + if (mLooper == nullptr) { mLooper = new Looper(true); - mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL); + mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, nullptr, nullptr); } return mLooper; } @@ -97,7 +97,7 @@ status_t SensorEventQueue::waitForEvent() const int events; int32_t result; do { - result = looper->pollOnce(-1, NULL, &events, NULL); + result = looper->pollOnce(-1, nullptr, &events, nullptr); if (result == ALOOPER_POLL_ERROR) { ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno); result = -EPIPE; // unknown error, so we make up one diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index b9ae524ee8..5840d51079 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -62,7 +62,7 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) // to the wrong package and stats based on app ops may be slightly off. if (opPackageName.size() <= 0) { sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); - if (binder != 0) { + if (binder != nullptr) { const uid_t uid = IPCThreadState::self()->getCallingUid(); Vector<String16> packages; interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages); @@ -93,7 +93,7 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) } SensorManager::SensorManager(const String16& opPackageName) - : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { + : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { // okay we're not locked here, but it's not needed during construction assertStateLocked(); } @@ -128,13 +128,13 @@ void SensorManager::sensorManagerDied() { Mutex::Autolock _l(mLock); mSensorServer.clear(); free(mSensorList); - mSensorList = NULL; + mSensorList = nullptr; mSensors.clear(); } status_t SensorManager::assertStateLocked() { bool initSensorManager = false; - if (mSensorServer == NULL) { + if (mSensorServer == nullptr) { initSensorManager = true; } else { // Ping binder to check if sensorservice is alive. @@ -164,7 +164,7 @@ status_t SensorManager::assertStateLocked() { size_t count = mSensors.size(); mSensorList = static_cast<Sensor const**>(malloc(count * sizeof(Sensor*))); - LOG_ALWAYS_FATAL_IF(mSensorList == NULL, "mSensorList NULL"); + LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL"); for (size_t i=0 ; i<count ; i++) { mSensorList[i] = mSensors.array() + i; @@ -222,7 +222,7 @@ Sensor const* SensorManager::getDefaultSensor(int type) } } } - return NULL; + return nullptr; } sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) { @@ -232,10 +232,10 @@ sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mo while (assertStateLocked() == NO_ERROR) { sp<ISensorEventConnection> connection = mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName); - if (connection == NULL) { + if (connection == nullptr) { // SensorService just died or the app doesn't have required permissions. ALOGE("createEventQueue: connection is NULL."); - return NULL; + return nullptr; } queue = new SensorEventQueue(connection); break; diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp new file mode 100644 index 0000000000..e0e3469fbb --- /dev/null +++ b/libs/sensorprivacy/Android.bp @@ -0,0 +1,47 @@ +// Copyright 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_shared { + name: "libsensorprivacy", + + aidl: { + export_aidl_headers: true, + local_include_dirs: ["aidl"], + }, + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-Wzero-as-null-pointer-constant", + ], + + srcs: [ + "aidl/android/hardware/ISensorPrivacyListener.aidl", + "aidl/android/hardware/ISensorPrivacyManager.aidl", + "SensorPrivacyManager.cpp", + ], + + shared_libs: [ + "libbinder", + "libcutils", + "libutils", + "liblog", + "libhardware", + ], + + export_include_dirs: ["include"], + + export_shared_lib_headers: ["libbinder"], +} diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp new file mode 100644 index 0000000000..1da79a0c8e --- /dev/null +++ b/libs/sensorprivacy/SensorPrivacyManager.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <mutex> +#include <unistd.h> + +#include <binder/Binder.h> +#include <binder/IServiceManager.h> +#include <sensorprivacy/SensorPrivacyManager.h> + +#include <utils/SystemClock.h> + +namespace android { + +SensorPrivacyManager::SensorPrivacyManager() +{ +} + +sp<hardware::ISensorPrivacyManager> SensorPrivacyManager::getService() +{ + std::lock_guard<Mutex> scoped_lock(mLock); + int64_t startTime = 0; + sp<hardware::ISensorPrivacyManager> service = mService; + while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { + sp<IBinder> binder = defaultServiceManager()->checkService(String16("sensor_privacy")); + if (binder == nullptr) { + // Wait for the sensor privacy service to come back... + if (startTime == 0) { + startTime = uptimeMillis(); + ALOGI("Waiting for sensor privacy service"); + } else if ((uptimeMillis() - startTime) > 1000000) { + ALOGW("Waiting too long for sensor privacy service, giving up"); + service = nullptr; + break; + } + usleep(25000); + } else { + service = interface_cast<hardware::ISensorPrivacyManager>(binder); + mService = service; + } + } + return service; +} + +void SensorPrivacyManager::addSensorPrivacyListener( + const sp<hardware::ISensorPrivacyListener>& listener) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + service->addSensorPrivacyListener(listener); + } +} + +void SensorPrivacyManager::removeSensorPrivacyListener( + const sp<hardware::ISensorPrivacyListener>& listener) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + service->removeSensorPrivacyListener(listener); + } +} + +bool SensorPrivacyManager::isSensorPrivacyEnabled() +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + bool result; + service->isSensorPrivacyEnabled(&result); + return result; + } + // if the SensorPrivacyManager is not available then assume sensor privacy is disabled + return false; +} + +}; // namespace android diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl new file mode 100644 index 0000000000..58177d837f --- /dev/null +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +/** + * @hide + */ +oneway interface ISensorPrivacyListener { + void onSensorPrivacyChanged(boolean enabled); +} diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl new file mode 100644 index 0000000000..4c2d5dbb8f --- /dev/null +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +import android.hardware.ISensorPrivacyListener; + +/** @hide */ +interface ISensorPrivacyManager { + void addSensorPrivacyListener(in ISensorPrivacyListener listener); + + void removeSensorPrivacyListener(in ISensorPrivacyListener listener); + + boolean isSensorPrivacyEnabled(); + + void setSensorPrivacy(boolean enable); +} diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h new file mode 100644 index 0000000000..882659598c --- /dev/null +++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SENSOR_PRIVACY_MANAGER_H +#define ANDROID_SENSOR_PRIVACY_MANAGER_H + +#include "android/hardware/ISensorPrivacyListener.h" +#include "android/hardware/ISensorPrivacyManager.h" + +#include <utils/threads.h> + +// --------------------------------------------------------------------------- +namespace android { + +class SensorPrivacyManager +{ +public: + SensorPrivacyManager(); + + void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener); + void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener); + bool isSensorPrivacyEnabled(); + +private: + Mutex mLock; + sp<hardware::ISensorPrivacyManager> mService; + sp<hardware::ISensorPrivacyManager> getService(); +}; + + +}; // namespace android +// --------------------------------------------------------------------------- + +#endif // ANDROID_SENSOR_PRIVACY_MANAGER_H diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index d25ad1a46d..956465c52a 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -53,6 +53,9 @@ cc_library_shared { srcs: [ "ColorSpace.cpp", + "BufferHubBuffer.cpp", + "BufferHubEventFd.cpp", + "BufferHubMetadata.cpp", "DebugUtils.cpp", "Fence.cpp", "FenceTime.cpp", @@ -65,6 +68,7 @@ cc_library_shared { "PixelFormat.cpp", "Rect.cpp", "Region.cpp", + "Transform.cpp", "UiConfig.cpp", ], @@ -74,7 +78,7 @@ cc_library_shared { shared_libs: [ "android.hardware.graphics.allocator@2.0", - "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@2.1", "android.hardware.configstore@1.0", @@ -89,10 +93,11 @@ cc_library_shared { "libutils", "libutilscallstack", "liblog", + "libpdx_default_transport", // TODO(b/112338294): Remove this once BufferHub moved to use Binder. ], export_shared_lib_headers: [ - "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", ], static_libs: [ @@ -101,9 +106,32 @@ cc_library_shared { "libmath", ], + // bufferhub is not used when building libgui for vendors + target: { + vendor: { + cflags: ["-DLIBUI_IN_VNDK"], + exclude_srcs: [ + "BufferHubBuffer.cpp", + "BufferHubEventFd.cpp", + "BufferHubMetadata.cpp", + ], + exclude_header_libs: [ + "libbufferhub_headers", + "libdvr_headers", + "libnativewindow_headers", + ], + exclude_shared_libs: [ + "libpdx_default_transport", + ], + }, + }, + header_libs: [ "libbase_headers", + "libbufferhub_headers", + "libdvr_headers", "libnativebase_headers", + "libnativewindow_headers", "libhardware_headers", "libui_headers", "libpdx_headers", @@ -120,6 +148,9 @@ cc_library_shared { "libhardware_headers", "libui_headers", ], + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, } cc_library_headers { @@ -128,6 +159,7 @@ cc_library_headers { vendor_available: true, target: { vendor: { + cflags: ["-DLIBUI_IN_VNDK"], override_export_include_dirs: ["include_vndk"], }, }, diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp new file mode 100644 index 0000000000..0582e1afe7 --- /dev/null +++ b/libs/ui/BufferHubBuffer.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// We would eliminate the clang warnings introduced by libdpx. +// TODO(b/112338294): Remove those once BufferHub moved to use Binder +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wdouble-promotion" +#pragma clang diagnostic ignored "-Wgnu-case-range" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#pragma clang diagnostic ignored "-Winconsistent-missing-destructor-override" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#pragma clang diagnostic ignored "-Wpacked" +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wundefined-func-template" +#pragma clang diagnostic ignored "-Wunused-template" +#pragma clang diagnostic ignored "-Wweak-vtables" +#include <pdx/default_transport/client_channel.h> +#include <pdx/default_transport/client_channel_factory.h> +#include <pdx/file_handle.h> +#include <private/dvr/bufferhub_rpc.h> +#pragma clang diagnostic pop + +#include <poll.h> + +#include <android-base/unique_fd.h> +#include <ui/BufferHubBuffer.h> +#include <ui/BufferHubDefs.h> + +using android::base::unique_fd; +using android::dvr::BufferTraits; +using android::dvr::DetachedBufferRPC; +using android::dvr::NativeHandleWrapper; + +// TODO(b/112338294): Remove PDX dependencies from libui. +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Status; +using android::pdx::default_transport::ClientChannel; +using android::pdx::default_transport::ClientChannelFactory; + +namespace android { + +namespace { + +// TODO(b/112338294): Remove this string literal after refactoring BufferHub +// to use Binder. +static constexpr char kBufferHubClientPath[] = "system/buffer_hub/client"; + +using BufferHubDefs::AnyClientAcquired; +using BufferHubDefs::AnyClientGained; +using BufferHubDefs::AnyClientPosted; +using BufferHubDefs::IsClientAcquired; +using BufferHubDefs::IsClientGained; +using BufferHubDefs::IsClientPosted; +using BufferHubDefs::IsClientReleased; +using BufferHubDefs::kHighBitsMask; + +} // namespace + +BufferHubClient::BufferHubClient() : Client(ClientChannelFactory::Create(kBufferHubClientPath)) {} + +BufferHubClient::BufferHubClient(LocalChannelHandle mChannelHandle) + : Client(ClientChannel::Create(std::move(mChannelHandle))) {} + +BufferHubClient::~BufferHubClient() {} + +bool BufferHubClient::IsValid() const { + return IsConnected() && GetChannelHandle().valid(); +} + +LocalChannelHandle BufferHubClient::TakeChannelHandle() { + if (IsConnected()) { + return std::move(GetChannelHandle()); + } else { + return {}; + } +} + +BufferHubBuffer::BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, + uint32_t format, uint64_t usage, size_t mUserMetadataSize) { + ATRACE_CALL(); + ALOGD("%s: width=%u height=%u layerCount=%u, format=%u usage=%" PRIx64 " mUserMetadataSize=%zu", + __FUNCTION__, width, height, layerCount, format, usage, mUserMetadataSize); + + auto status = + mClient.InvokeRemoteMethod<DetachedBufferRPC::Create>(width, height, layerCount, format, + usage, mUserMetadataSize); + if (!status) { + ALOGE("%s: Failed to create detached buffer: %s", __FUNCTION__, + status.GetErrorMessage().c_str()); + mClient.Close(-status.error()); + } + + const int ret = ImportGraphicBuffer(); + if (ret < 0) { + ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-ret)); + mClient.Close(ret); + } +} + +BufferHubBuffer::BufferHubBuffer(LocalChannelHandle mChannelHandle) + : mClient(std::move(mChannelHandle)) { + const int ret = ImportGraphicBuffer(); + if (ret < 0) { + ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-ret)); + mClient.Close(ret); + } +} + +int BufferHubBuffer::ImportGraphicBuffer() { + ATRACE_CALL(); + + auto status = mClient.InvokeRemoteMethod<DetachedBufferRPC::Import>(); + if (!status) { + ALOGE("%s: Failed to import GraphicBuffer: %s", __FUNCTION__, + status.GetErrorMessage().c_str()); + return -status.error(); + } + + BufferTraits<LocalHandle> bufferTraits = status.take(); + if (bufferTraits.id() < 0) { + ALOGE("%s: Received an invalid id!", __FUNCTION__); + return -EIO; + } + + // Stash the buffer id to replace the value in mId. + const int bufferId = bufferTraits.id(); + + // Import the metadata. + LocalHandle metadataHandle = bufferTraits.take_metadata_handle(); + unique_fd metadataFd(metadataHandle.Release()); + mMetadata = BufferHubMetadata::Import(std::move(metadataFd)); + + if (!mMetadata.IsValid()) { + ALOGE("%s: invalid metadata.", __FUNCTION__); + return -ENOMEM; + } + + if (mMetadata.metadata_size() != bufferTraits.metadata_size()) { + ALOGE("%s: metadata buffer too small: %zu, expected: %" PRIu64 ".", __FUNCTION__, + mMetadata.metadata_size(), bufferTraits.metadata_size()); + return -ENOMEM; + } + + size_t metadataSize = static_cast<size_t>(bufferTraits.metadata_size()); + if (metadataSize < BufferHubDefs::kMetadataHeaderSize) { + ALOGE("%s: metadata too small: %zu", __FUNCTION__, metadataSize); + return -EINVAL; + } + + // Populate shortcuts to the atomics in metadata. + auto metadata_header = mMetadata.metadata_header(); + buffer_state_ = &metadata_header->buffer_state; + fence_state_ = &metadata_header->fence_state; + active_clients_bit_mask_ = &metadata_header->active_clients_bit_mask; + // The C++ standard recommends (but does not require) that lock-free atomic operations are + // also address-free, that is, suitable for communication between processes using shared + // memory. + LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(buffer_state_) || + !std::atomic_is_lock_free(fence_state_) || + !std::atomic_is_lock_free(active_clients_bit_mask_), + "Atomic variables in ashmen are not lock free."); + + // Import the buffer: We only need to hold on the native_handle_t here so that + // GraphicBuffer instance can be created in future. + mBufferHandle = bufferTraits.take_buffer_handle(); + + // Populate buffer desc based on buffer traits. + mBufferDesc.width = bufferTraits.width(); + mBufferDesc.height = bufferTraits.height(); + mBufferDesc.layers = bufferTraits.layer_count(); + mBufferDesc.format = bufferTraits.format(); + mBufferDesc.usage = bufferTraits.usage(); + mBufferDesc.stride = bufferTraits.stride(); + mBufferDesc.rfu0 = 0U; + mBufferDesc.rfu1 = 0U; + + // If all imports succeed, replace the previous buffer and id. + mId = bufferId; + mClientStateMask = bufferTraits.client_state_mask(); + + // TODO(b/112012161) Set up shared fences. + ALOGD("%s: id=%d, buffer_state=%" PRIx32 ".", __FUNCTION__, id(), + buffer_state_->load(std::memory_order_acquire)); + return 0; +} + +int BufferHubBuffer::Gain() { + uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientGained(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already gained by this client %" PRIx32 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + do { + if (AnyClientGained(current_buffer_state & (~mClientStateMask)) || + AnyClientAcquired(current_buffer_state)) { + ALOGE("%s: Buffer is in use, id=%d mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Change the buffer state to gained state, whose value happens to be the same as + // mClientStateMask. + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, mClientStateMask, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence. + return 0; +} + +int BufferHubBuffer::Post() { + uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + uint32_t current_active_clients_bit_mask = 0U; + uint32_t updated_buffer_state = 0U; + do { + if (!IsClientGained(current_buffer_state, mClientStateMask)) { + ALOGE("%s: Cannot post a buffer that is not gained by this client. buffer_id=%d " + "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Set the producer client buffer state to released, other clients' buffer state to posted. + current_active_clients_bit_mask = active_clients_bit_mask_->load(std::memory_order_acquire); + updated_buffer_state = + current_active_clients_bit_mask & (~mClientStateMask) & kHighBitsMask; + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence if needed. + return 0; +} + +int BufferHubBuffer::Acquire() { + uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientAcquired(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already acquired by this client %" PRIx32 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + uint32_t updated_buffer_state = 0U; + do { + if (!IsClientPosted(current_buffer_state, mClientStateMask)) { + ALOGE("%s: Cannot acquire a buffer that is not in posted state. buffer_id=%d " + "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Change the buffer state for this consumer from posted to acquired. + updated_buffer_state = current_buffer_state ^ mClientStateMask; + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence. + return 0; +} + +int BufferHubBuffer::Release() { + uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientReleased(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already released by this client %" PRIx32 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + uint32_t updated_buffer_state = 0U; + do { + updated_buffer_state = current_buffer_state & (~mClientStateMask); + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence if needed. + return 0; +} + +int BufferHubBuffer::Poll(int timeoutMs) { + ATRACE_CALL(); + + pollfd p = {mClient.event_fd(), POLLIN, 0}; + return poll(&p, 1, timeoutMs); +} + +Status<LocalChannelHandle> BufferHubBuffer::Duplicate() { + ATRACE_CALL(); + ALOGD("%s: id=%d.", __FUNCTION__, mId); + + auto statusOrHandle = mClient.InvokeRemoteMethod<DetachedBufferRPC::Duplicate>(); + + if (!statusOrHandle.ok()) { + ALOGE("%s: Failed to duplicate buffer (id=%d): %s.", __FUNCTION__, mId, + statusOrHandle.GetErrorMessage().c_str()); + } + return statusOrHandle; +} + +} // namespace android diff --git a/libs/ui/BufferHubEventFd.cpp b/libs/ui/BufferHubEventFd.cpp new file mode 100644 index 0000000000..978b3526f3 --- /dev/null +++ b/libs/ui/BufferHubEventFd.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/eventfd.h> + +#include <log/log.h> +#include <ui/BufferHubEventFd.h> + +namespace android { + +BufferHubEventFd::BufferHubEventFd() : mFd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) {} + +status_t BufferHubEventFd::signal() const { + if (!isValid()) { + ALOGE("%s: cannot signal an invalid eventfd.", __FUNCTION__); + return DEAD_OBJECT; + } + + eventfd_write(mFd.get(), 1); + return OK; +} + +status_t BufferHubEventFd::clear() const { + if (!isValid()) { + ALOGE("%s: cannot clear an invalid eventfd.", __FUNCTION__); + return DEAD_OBJECT; + } + + eventfd_t value; + eventfd_read(mFd.get(), &value); + return OK; +} + +} // namespace android diff --git a/libs/ui/BufferHubMetadata.cpp b/libs/ui/BufferHubMetadata.cpp new file mode 100644 index 0000000000..816707db9d --- /dev/null +++ b/libs/ui/BufferHubMetadata.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <sys/mman.h> +#include <limits> + +#include <cutils/ashmem.h> +#include <log/log.h> +#include <ui/BufferHubMetadata.h> + +namespace android { + +namespace { + +static const int kAshmemProt = PROT_READ | PROT_WRITE; + +} // namespace + +using BufferHubDefs::kMetadataHeaderSize; +using BufferHubDefs::MetadataHeader; + +/* static */ +BufferHubMetadata BufferHubMetadata::Create(size_t userMetadataSize) { + // The size the of metadata buffer is used as the "width" parameter during allocation. Thus it + // cannot overflow uint32_t. + if (userMetadataSize >= (std::numeric_limits<uint32_t>::max() - kMetadataHeaderSize)) { + ALOGE("BufferHubMetadata::Create: metadata size too big: %zu.", userMetadataSize); + return {}; + } + + const size_t metadataSize = userMetadataSize + kMetadataHeaderSize; + int fd = ashmem_create_region(/*name=*/"BufferHubMetadata", metadataSize); + if (fd < 0) { + ALOGE("BufferHubMetadata::Create: failed to create ashmem region."); + return {}; + } + + // Hand over the ownership of the fd to a unique_fd immediately after the successful + // return of ashmem_create_region. The ashmemFd is going to own the fd and to prevent fd + // leaks during error handling. + unique_fd ashmemFd{fd}; + + if (ashmem_set_prot_region(ashmemFd.get(), kAshmemProt) != 0) { + ALOGE("BufferHubMetadata::Create: failed to set protect region."); + return {}; + } + + return BufferHubMetadata::Import(std::move(ashmemFd)); +} + +/* static */ +BufferHubMetadata BufferHubMetadata::Import(unique_fd ashmemFd) { + if (!ashmem_valid(ashmemFd.get())) { + ALOGE("BufferHubMetadata::Import: invalid ashmem fd."); + return {}; + } + + size_t metadataSize = static_cast<size_t>(ashmem_get_size_region(ashmemFd.get())); + size_t userMetadataSize = metadataSize - kMetadataHeaderSize; + + // Note that here the buffer state is mapped from shared memory as an atomic object. The + // std::atomic's constructor will not be called so that the original value stored in the memory + // region can be preserved. + auto metadataHeader = static_cast<MetadataHeader*>(mmap(nullptr, metadataSize, kAshmemProt, + MAP_SHARED, ashmemFd.get(), + /*offset=*/0)); + if (metadataHeader == nullptr) { + ALOGE("BufferHubMetadata::Import: failed to map region."); + return {}; + } + + return BufferHubMetadata(userMetadataSize, std::move(ashmemFd), metadataHeader); +} + +BufferHubMetadata::BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd, + MetadataHeader* metadataHeader) + : mUserMetadataSize(userMetadataSize), + mAshmemFd(std::move(ashmemFd)), + mMetadataHeader(metadataHeader) {} + +BufferHubMetadata::~BufferHubMetadata() { + if (mMetadataHeader != nullptr) { + int ret = munmap(mMetadataHeader, metadata_size()); + ALOGE_IF(ret != 0, + "BufferHubMetadata::~BufferHubMetadata: failed to unmap ashmem, error=%d.", errno); + mMetadataHeader = nullptr; + } +} + +} // namespace android diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp index 61df02d41d..ee06d930d8 100644 --- a/libs/ui/DebugUtils.cpp +++ b/libs/ui/DebugUtils.cpp @@ -234,6 +234,9 @@ std::string decodeColorMode(ColorMode colorMode) { case ColorMode::BT2020: return std::string("ColorMode::BT2020"); + case ColorMode::DISPLAY_BT2020: + return std::string("ColorMode::DISPLAY_BT2020"); + case ColorMode::BT2100_PQ: return std::string("ColorMode::BT2100_PQ"); diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp index 14147663de..340231d352 100644 --- a/libs/ui/FenceTime.cpp +++ b/libs/ui/FenceTime.cpp @@ -33,18 +33,6 @@ namespace android { const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE); -void* FenceTime::operator new(size_t byteCount) noexcept { - void *p = nullptr; - if (posix_memalign(&p, alignof(FenceTime), byteCount)) { - return nullptr; - } - return p; -} - -void FenceTime::operator delete(void *p) { - free(p); -} - FenceTime::FenceTime(const sp<Fence>& fence) : mState(((fence.get() != nullptr) && fence->isValid()) ? State::VALID : State::INVALID), diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp index 37cf617374..20f27c53e3 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp @@ -42,9 +42,6 @@ uint64_t getValid10UsageBits() { for (const auto bit : hardware::hidl_enum_range<BufferUsage>()) { bits = bits | bit; } - // TODO(b/72323293, b/72703005): Remove these additional bits - bits = bits | (1 << 10) | (1 << 13); - return bits; }(); return valid10UsageBits; diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 4aa9f628ce..f408fcbe84 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -22,7 +22,10 @@ #include <grallocusage/GrallocUsageConversion.h> -#include <ui/DetachedBufferHandle.h> +#ifndef LIBUI_IN_VNDK +#include <ui/BufferHubBuffer.h> +#endif // LIBUI_IN_VNDK + #include <ui/Gralloc2.h> #include <ui/GraphicBufferAllocator.h> #include <ui/GraphicBufferMapper.h> @@ -90,6 +93,22 @@ GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod m inUsage, inStride); } +#ifndef LIBUI_IN_VNDK +GraphicBuffer::GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer) : GraphicBuffer() { + if (buffer == nullptr) { + mInitCheck = BAD_VALUE; + return; + } + + mInitCheck = initWithHandle(buffer->DuplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE, + buffer->desc().width, buffer->desc().height, + static_cast<PixelFormat>(buffer->desc().format), + buffer->desc().layers, buffer->desc().usage, buffer->desc().stride); + mBufferId = buffer->id(); + mBufferHubBuffer = std::move(buffer); +} +#endif // LIBUI_IN_VNDK + GraphicBuffer::~GraphicBuffer() { if (handle) { @@ -484,23 +503,11 @@ status_t GraphicBuffer::unflatten( return NO_ERROR; } -bool GraphicBuffer::isDetachedBuffer() const { - return mDetachedBufferHandle && mDetachedBufferHandle->isValid(); -} - -status_t GraphicBuffer::setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> channel) { - if (isDetachedBuffer()) { - ALOGW("setDetachedBuffer: there is already a BufferHub channel associated with this " - "GraphicBuffer. Replacing the old one."); - } - - mDetachedBufferHandle = std::move(channel); - return NO_ERROR; -} - -std::unique_ptr<DetachedBufferHandle> GraphicBuffer::takeDetachedBufferHandle() { - return std::move(mDetachedBufferHandle); +#ifndef LIBUI_IN_VNDK +bool GraphicBuffer::isBufferHubBuffer() const { + return mBufferHubBuffer != nullptr; } +#endif // LIBUI_IN_VNDK // --------------------------------------------------------------------------- diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index eaba1ed1aa..f56e6b9e86 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -24,9 +24,9 @@ #include <grallocusage/GrallocUsageConversion.h> +#include <android-base/stringprintf.h> #include <log/log.h> #include <utils/Singleton.h> -#include <utils/String8.h> #include <utils/Trace.h> #include <ui/Gralloc2.h> @@ -35,6 +35,8 @@ namespace android { // --------------------------------------------------------------------------- +using base::StringAppendF; + ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferAllocator ) Mutex GraphicBufferAllocator::sLock; @@ -50,46 +52,37 @@ GraphicBufferAllocator::GraphicBufferAllocator() GraphicBufferAllocator::~GraphicBufferAllocator() {} -void GraphicBufferAllocator::dump(String8& result) const -{ +void GraphicBufferAllocator::dump(std::string& result) const { Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); size_t total = 0; - const size_t SIZE = 4096; - char buffer[SIZE]; - snprintf(buffer, SIZE, "Allocated buffers:\n"); - result.append(buffer); + result.append("Allocated buffers:\n"); const size_t c = list.size(); for (size_t i=0 ; i<c ; i++) { const alloc_rec_t& rec(list.valueAt(i)); if (rec.size) { - snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 - " | %s\n", - list.keyAt(i), rec.size/1024.0, - rec.width, rec.stride, rec.height, rec.layerCount, rec.format, - rec.usage, rec.requestorName.c_str()); + StringAppendF(&result, + "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n", + list.keyAt(i), rec.size / 1024.0, rec.width, rec.stride, rec.height, + rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str()); } else { - snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 - " | %s\n", - list.keyAt(i), - rec.width, rec.stride, rec.height, rec.layerCount, rec.format, - rec.usage, rec.requestorName.c_str()); + StringAppendF(&result, + "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n", + list.keyAt(i), rec.width, rec.stride, rec.height, rec.layerCount, + rec.format, rec.usage, rec.requestorName.c_str()); } - result.append(buffer); total += rec.size; } - snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0); - result.append(buffer); + StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", total / 1024.0); - std::string deviceDump = mAllocator->dumpDebugInfo(); - result.append(deviceDump.c_str(), deviceDump.size()); + result.append(mAllocator->dumpDebugInfo()); } void GraphicBufferAllocator::dumpToSystemLog() { - String8 s; + std::string s; GraphicBufferAllocator::getInstance().dump(s); - ALOGD("%s", s.string()); + ALOGD("%s", s.c_str()); } status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, @@ -108,6 +101,9 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, if (layerCount < 1) layerCount = 1; + // TODO(b/72323293, b/72703005): Remove these invalid bits from callers + usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13)); + Gralloc2::IMapper::BufferDescriptorInfo info = {}; info.width = width; info.height = height; diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp index d8702e5755..13fed3a239 100644 --- a/libs/ui/Rect.cpp +++ b/libs/ui/Rect.cpp @@ -72,6 +72,14 @@ Rect& Rect::offsetBy(int32_t x, int32_t y) { return *this; } +Rect& Rect::inset(int32_t _left, int32_t _top, int32_t _right, int32_t _bottom) { + this->left += _left; + this->top += _top; + this->right -= _right; + this->bottom -= _bottom; + return *this; +} + const Rect Rect::operator +(const Point& rhs) const { const Rect result(left + rhs.x, top + rhs.y, right + rhs.x, bottom + rhs.y); return result; diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index fe4ae6c414..3bd3748439 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -19,8 +19,9 @@ #include <inttypes.h> #include <limits.h> +#include <android-base/stringprintf.h> + #include <utils/Log.h> -#include <utils/String8.h> #include <utils/CallStack.h> #include <ui/Rect.h> @@ -41,6 +42,8 @@ namespace android { // ---------------------------------------------------------------------------- +using base::StringAppendF; + enum { op_nand = region_operator<Rect>::op_nand, op_and = region_operator<Rect>::op_and, @@ -325,6 +328,20 @@ Region& Region::translateSelf(int x, int y) { return *this; } +Region& Region::scaleSelf(float sx, float sy) { + size_t count = mStorage.size(); + Rect* rects = mStorage.editArray(); + while (count) { + rects->left = static_cast<int32_t>(rects->left * sx + 0.5f); + rects->right = static_cast<int32_t>(rects->right * sx + 0.5f); + rects->top = static_cast<int32_t>(rects->top * sy + 0.5f); + rects->bottom = static_cast<int32_t>(rects->bottom * sy + 0.5f); + rects++; + count--; + } + return *this; +} + // ---------------------------------------------------------------------------- const Region Region::merge(const Rect& rhs) const { @@ -854,16 +871,14 @@ Rect const* Region::getArray(size_t* count) const { // ---------------------------------------------------------------------------- -void Region::dump(String8& out, const char* what, uint32_t /* flags */) const -{ +void Region::dump(std::string& out, const char* what, uint32_t /* flags */) const { const_iterator head = begin(); const_iterator const tail = end(); - out.appendFormat(" Region %s (this=%p, count=%" PRIdPTR ")\n", - what, this, tail - head); + StringAppendF(&out, " Region %s (this=%p, count=%" PRIdPTR ")\n", what, this, tail - head); while (head != tail) { - out.appendFormat(" [%3d, %3d, %3d, %3d]\n", head->left, head->top, - head->right, head->bottom); + StringAppendF(&out, " [%3d, %3d, %3d, %3d]\n", head->left, head->top, head->right, + head->bottom); ++head; } } diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp new file mode 100644 index 0000000000..25128effaf --- /dev/null +++ b/libs/ui/Transform.cpp @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2007 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 <math.h> + +#include <cutils/compiler.h> +#include <ui/Region.h> +#include <ui/Transform.h> +#include <utils/String8.h> + +namespace android { +namespace ui { + +Transform::Transform() { + reset(); +} + +Transform::Transform(const Transform& other) + : mMatrix(other.mMatrix), mType(other.mType) { +} + +Transform::Transform(uint32_t orientation) { + set(orientation, 0, 0); +} + +Transform::~Transform() = default; + +static const float EPSILON = 0.0f; + +bool Transform::isZero(float f) { + return fabs(f) <= EPSILON; +} + +bool Transform::absIsOne(float f) { + return isZero(fabs(f) - 1.0f); +} + +Transform Transform::operator * (const Transform& rhs) const +{ + if (CC_LIKELY(mType == IDENTITY)) + return rhs; + + Transform r(*this); + if (rhs.mType == IDENTITY) + return r; + + // TODO: we could use mType to optimize the matrix multiply + const mat33& A(mMatrix); + const mat33& B(rhs.mMatrix); + mat33& D(r.mMatrix); + for (size_t i = 0; i < 3; i++) { + const float v0 = A[0][i]; + const float v1 = A[1][i]; + const float v2 = A[2][i]; + D[0][i] = v0*B[0][0] + v1*B[0][1] + v2*B[0][2]; + D[1][i] = v0*B[1][0] + v1*B[1][1] + v2*B[1][2]; + D[2][i] = v0*B[2][0] + v1*B[2][1] + v2*B[2][2]; + } + r.mType |= rhs.mType; + + // TODO: we could recompute this value from r and rhs + r.mType &= 0xFF; + r.mType |= UNKNOWN_TYPE; + return r; +} + +Transform& Transform::operator=(const Transform& other) { + mMatrix = other.mMatrix; + mType = other.mType; + return *this; +} + +const vec3& Transform::operator [] (size_t i) const { + return mMatrix[i]; +} + +float Transform::tx() const { + return mMatrix[2][0]; +} + +float Transform::ty() const { + return mMatrix[2][1]; +} + +float Transform::sx() const { + return mMatrix[0][0]; +} + +float Transform::sy() const { + return mMatrix[1][1]; +} + +void Transform::reset() { + mType = IDENTITY; + for(size_t i = 0; i < 3; i++) { + vec3& v(mMatrix[i]); + for (size_t j = 0; j < 3; j++) + v[j] = ((i == j) ? 1.0f : 0.0f); + } +} + +void Transform::set(float tx, float ty) +{ + mMatrix[2][0] = tx; + mMatrix[2][1] = ty; + mMatrix[2][2] = 1.0f; + + if (isZero(tx) && isZero(ty)) { + mType &= ~TRANSLATE; + } else { + mType |= TRANSLATE; + } +} + +void Transform::set(float a, float b, float c, float d) +{ + mat33& M(mMatrix); + M[0][0] = a; M[1][0] = b; + M[0][1] = c; M[1][1] = d; + M[0][2] = 0; M[1][2] = 0; + mType = UNKNOWN_TYPE; +} + +status_t Transform::set(uint32_t flags, float w, float h) +{ + if (flags & ROT_INVALID) { + // that's not allowed! + reset(); + return BAD_VALUE; + } + + Transform H, V, R; + if (flags & ROT_90) { + // w & h are inverted when rotating by 90 degrees + std::swap(w, h); + } + + if (flags & FLIP_H) { + H.mType = (FLIP_H << 8) | SCALE; + H.mType |= isZero(w) ? IDENTITY : TRANSLATE; + mat33& M(H.mMatrix); + M[0][0] = -1; + M[2][0] = w; + } + + if (flags & FLIP_V) { + V.mType = (FLIP_V << 8) | SCALE; + V.mType |= isZero(h) ? IDENTITY : TRANSLATE; + mat33& M(V.mMatrix); + M[1][1] = -1; + M[2][1] = h; + } + + if (flags & ROT_90) { + const float original_w = h; + R.mType = (ROT_90 << 8) | ROTATE; + R.mType |= isZero(original_w) ? IDENTITY : TRANSLATE; + mat33& M(R.mMatrix); + M[0][0] = 0; M[1][0] =-1; M[2][0] = original_w; + M[0][1] = 1; M[1][1] = 0; + } + + *this = (R*(H*V)); + return NO_ERROR; +} + +vec2 Transform::transform(const vec2& v) const { + vec2 r; + const mat33& M(mMatrix); + r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]; + r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]; + return r; +} + +vec3 Transform::transform(const vec3& v) const { + vec3 r; + const mat33& M(mMatrix); + r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]*v[2]; + r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]*v[2]; + r[2] = M[0][2]*v[0] + M[1][2]*v[1] + M[2][2]*v[2]; + return r; +} + +vec2 Transform::transform(int x, int y) const +{ + return transform(vec2(x,y)); +} + +Rect Transform::makeBounds(int w, int h) const +{ + return transform( Rect(w, h) ); +} + +Rect Transform::transform(const Rect& bounds, bool roundOutwards) const +{ + Rect r; + vec2 lt( bounds.left, bounds.top ); + vec2 rt( bounds.right, bounds.top ); + vec2 lb( bounds.left, bounds.bottom ); + vec2 rb( bounds.right, bounds.bottom ); + + lt = transform(lt); + rt = transform(rt); + lb = transform(lb); + rb = transform(rb); + + if (roundOutwards) { + r.left = static_cast<int32_t>(floorf(std::min({lt[0], rt[0], lb[0], rb[0]}))); + r.top = static_cast<int32_t>(floorf(std::min({lt[1], rt[1], lb[1], rb[1]}))); + r.right = static_cast<int32_t>(ceilf(std::max({lt[0], rt[0], lb[0], rb[0]}))); + r.bottom = static_cast<int32_t>(ceilf(std::max({lt[1], rt[1], lb[1], rb[1]}))); + } else { + r.left = static_cast<int32_t>(floorf(std::min({lt[0], rt[0], lb[0], rb[0]}) + 0.5f)); + r.top = static_cast<int32_t>(floorf(std::min({lt[1], rt[1], lb[1], rb[1]}) + 0.5f)); + r.right = static_cast<int32_t>(floorf(std::max({lt[0], rt[0], lb[0], rb[0]}) + 0.5f)); + r.bottom = static_cast<int32_t>(floorf(std::max({lt[1], rt[1], lb[1], rb[1]}) + 0.5f)); + } + + return r; +} + +FloatRect Transform::transform(const FloatRect& bounds) const +{ + vec2 lt(bounds.left, bounds.top); + vec2 rt(bounds.right, bounds.top); + vec2 lb(bounds.left, bounds.bottom); + vec2 rb(bounds.right, bounds.bottom); + + lt = transform(lt); + rt = transform(rt); + lb = transform(lb); + rb = transform(rb); + + FloatRect r; + r.left = std::min({lt[0], rt[0], lb[0], rb[0]}); + r.top = std::min({lt[1], rt[1], lb[1], rb[1]}); + r.right = std::max({lt[0], rt[0], lb[0], rb[0]}); + r.bottom = std::max({lt[1], rt[1], lb[1], rb[1]}); + + return r; +} + +Region Transform::transform(const Region& reg) const +{ + Region out; + if (CC_UNLIKELY(type() > TRANSLATE)) { + if (CC_LIKELY(preserveRects())) { + Region::const_iterator it = reg.begin(); + Region::const_iterator const end = reg.end(); + while (it != end) { + out.orSelf(transform(*it++)); + } + } else { + out.set(transform(reg.bounds())); + } + } else { + int xpos = static_cast<int>(floorf(tx() + 0.5f)); + int ypos = static_cast<int>(floorf(ty() + 0.5f)); + out = reg.translate(xpos, ypos); + } + return out; +} + +uint32_t Transform::type() const +{ + if (mType & UNKNOWN_TYPE) { + // recompute what this transform is + + const mat33& M(mMatrix); + const float a = M[0][0]; + const float b = M[1][0]; + const float c = M[0][1]; + const float d = M[1][1]; + const float x = M[2][0]; + const float y = M[2][1]; + + bool scale = false; + uint32_t flags = ROT_0; + if (isZero(b) && isZero(c)) { + if (a<0) flags |= FLIP_H; + if (d<0) flags |= FLIP_V; + if (!absIsOne(a) || !absIsOne(d)) { + scale = true; + } + } else if (isZero(a) && isZero(d)) { + flags |= ROT_90; + if (b>0) flags |= FLIP_V; + if (c<0) flags |= FLIP_H; + if (!absIsOne(b) || !absIsOne(c)) { + scale = true; + } + } else { + // there is a skew component and/or a non 90 degrees rotation + flags = ROT_INVALID; + } + + mType = flags << 8; + if (flags & ROT_INVALID) { + mType |= UNKNOWN; + } else { + if ((flags & ROT_90) || ((flags & ROT_180) == ROT_180)) + mType |= ROTATE; + if (flags & FLIP_H) + mType ^= SCALE; + if (flags & FLIP_V) + mType ^= SCALE; + if (scale) + mType |= SCALE; + } + + if (!isZero(x) || !isZero(y)) + mType |= TRANSLATE; + } + return mType; +} + +Transform Transform::inverse() const { + // our 3x3 matrix is always of the form of a 2x2 transformation + // followed by a translation: T*M, therefore: + // (T*M)^-1 = M^-1 * T^-1 + Transform result; + if (mType <= TRANSLATE) { + // 1 0 0 + // 0 1 0 + // x y 1 + result = *this; + result.mMatrix[2][0] = -result.mMatrix[2][0]; + result.mMatrix[2][1] = -result.mMatrix[2][1]; + } else { + // a c 0 + // b d 0 + // x y 1 + const mat33& M(mMatrix); + const float a = M[0][0]; + const float b = M[1][0]; + const float c = M[0][1]; + const float d = M[1][1]; + const float x = M[2][0]; + const float y = M[2][1]; + + const float idet = 1.0f / (a*d - b*c); + result.mMatrix[0][0] = d*idet; + result.mMatrix[0][1] = -c*idet; + result.mMatrix[1][0] = -b*idet; + result.mMatrix[1][1] = a*idet; + result.mType = mType; + + vec2 T(-x, -y); + T = result.transform(T); + result.mMatrix[2][0] = T[0]; + result.mMatrix[2][1] = T[1]; + } + return result; +} + +uint32_t Transform::getType() const { + return type() & 0xFF; +} + +uint32_t Transform::getOrientation() const +{ + return (type() >> 8) & 0xFF; +} + +bool Transform::preserveRects() const +{ + return (getOrientation() & ROT_INVALID) ? false : true; +} + +void Transform::dump(const char* name) const +{ + type(); // updates the type + + String8 flags, type; + const mat33& m(mMatrix); + uint32_t orient = mType >> 8; + + if (orient&ROT_INVALID) { + flags.append("ROT_INVALID "); + } else { + if (orient&ROT_90) { + flags.append("ROT_90 "); + } else { + flags.append("ROT_0 "); + } + if (orient&FLIP_V) + flags.append("FLIP_V "); + if (orient&FLIP_H) + flags.append("FLIP_H "); + } + + if (!(mType&(SCALE|ROTATE|TRANSLATE))) + type.append("IDENTITY "); + if (mType&SCALE) + type.append("SCALE "); + if (mType&ROTATE) + type.append("ROTATE "); + if (mType&TRANSLATE) + type.append("TRANSLATE "); + + ALOGD("%s 0x%08x (%s, %s)", name, mType, flags.string(), type.string()); + ALOGD("%.4f %.4f %.4f", static_cast<double>(m[0][0]), static_cast<double>(m[1][0]), + static_cast<double>(m[2][0])); + ALOGD("%.4f %.4f %.4f", static_cast<double>(m[0][1]), static_cast<double>(m[1][1]), + static_cast<double>(m[2][1])); + ALOGD("%.4f %.4f %.4f", static_cast<double>(m[0][2]), static_cast<double>(m[1][2]), + static_cast<double>(m[2][2])); +} + +} // namespace ui +} // namespace android diff --git a/libs/ui/UiConfig.cpp b/libs/ui/UiConfig.cpp index 7730690cfd..0ac863d718 100644 --- a/libs/ui/UiConfig.cpp +++ b/libs/ui/UiConfig.cpp @@ -18,8 +18,7 @@ namespace android { -void appendUiConfigString(String8& configStr) -{ +void appendUiConfigString(std::string& configStr) { static const char* config = " [libui]"; configStr.append(config); diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h new file mode 100644 index 0000000000..90dd391d8d --- /dev/null +++ b/libs/ui/include/ui/BufferHubBuffer.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BUFFER_HUB_BUFFER_H_ +#define ANDROID_BUFFER_HUB_BUFFER_H_ + +// We would eliminate the clang warnings introduced by libdpx. +// TODO(b/112338294): Remove those once BufferHub moved to use Binder +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wdouble-promotion" +#pragma clang diagnostic ignored "-Wgnu-case-range" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#pragma clang diagnostic ignored "-Winconsistent-missing-destructor-override" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#pragma clang diagnostic ignored "-Wpacked" +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wundefined-func-template" +#pragma clang diagnostic ignored "-Wunused-template" +#pragma clang diagnostic ignored "-Wweak-vtables" +#include <pdx/client.h> +#include <private/dvr/native_handle_wrapper.h> +#pragma clang diagnostic pop + +#include <android/hardware_buffer.h> +#include <ui/BufferHubDefs.h> +#include <ui/BufferHubMetadata.h> + +namespace android { + +class BufferHubClient : public pdx::Client { +public: + BufferHubClient(); + virtual ~BufferHubClient(); + explicit BufferHubClient(pdx::LocalChannelHandle mChannelHandle); + + bool IsValid() const; + pdx::LocalChannelHandle TakeChannelHandle(); + + using pdx::Client::Close; + using pdx::Client::event_fd; + using pdx::Client::GetChannel; + using pdx::Client::InvokeRemoteMethod; +}; + +class BufferHubBuffer { +public: + // Allocates a standalone BufferHubBuffer not associated with any producer consumer set. + static std::unique_ptr<BufferHubBuffer> Create(uint32_t width, uint32_t height, + uint32_t layerCount, uint32_t format, + uint64_t usage, size_t userMetadataSize) { + return std::unique_ptr<BufferHubBuffer>( + new BufferHubBuffer(width, height, layerCount, format, usage, userMetadataSize)); + } + + // Imports the given channel handle to a BufferHubBuffer, taking ownership. + static std::unique_ptr<BufferHubBuffer> Import(pdx::LocalChannelHandle mChannelHandle) { + return std::unique_ptr<BufferHubBuffer>(new BufferHubBuffer(std::move(mChannelHandle))); + } + + BufferHubBuffer(const BufferHubBuffer&) = delete; + void operator=(const BufferHubBuffer&) = delete; + + // Gets ID of the buffer client. All BufferHubBuffer clients derived from the same buffer in + // bufferhubd share the same buffer id. + int id() const { return mId; } + + // Returns the buffer description, which is guaranteed to be faithful values from bufferhubd. + const AHardwareBuffer_Desc& desc() const { return mBufferDesc; } + + const native_handle_t* DuplicateHandle() { return mBufferHandle.DuplicateHandle(); } + + // Returns the current value of MetadataHeader::buffer_state. + uint32_t buffer_state() { + return mMetadata.metadata_header()->buffer_state.load(std::memory_order_acquire); + } + + // A state mask which is unique to a buffer hub client among all its siblings sharing the same + // concrete graphic buffer. + uint32_t client_state_mask() const { return mClientStateMask; } + + size_t user_metadata_size() const { return mMetadata.user_metadata_size(); } + + // Returns true if the buffer holds an open PDX channels towards bufferhubd. + bool IsConnected() const { return mClient.IsValid(); } + + // Returns true if the buffer holds an valid native buffer handle that's availble for the client + // to read from and/or write into. + bool IsValid() const { return mBufferHandle.IsValid(); } + + // Gains the buffer for exclusive write permission. Read permission is implied once a buffer is + // gained. + // The buffer can be gained as long as there is no other client in acquired or gained state. + int Gain(); + + // Posts the gained buffer for other buffer clients to use the buffer. + // The buffer can be posted iff the buffer state for this client is gained. + // After posting the buffer, this client is put to released state and does not have access to + // the buffer for this cycle of the usage of the buffer. + int Post(); + + // Acquires the buffer for shared read permission. + // The buffer can be acquired iff the buffer state for this client is posted. + int Acquire(); + + // Releases the buffer. + // The buffer can be released from any buffer state. + // After releasing the buffer, this client no longer have any permissions to the buffer for the + // current cycle of the usage of the buffer. + int Release(); + + // Returns the event mask for all the events that are pending on this buffer (see sys/poll.h for + // all possible bits). + pdx::Status<int> GetEventMask(int events) { + if (auto* channel = mClient.GetChannel()) { + return channel->GetEventMask(events); + } else { + return pdx::ErrorStatus(EINVAL); + } + } + + // Polls the fd for |timeoutMs| milliseconds (-1 for infinity). + int Poll(int timeoutMs); + + // Creates a BufferHubBuffer client from an existing one. The new client will + // share the same underlying gralloc buffer and ashmem region for metadata. + pdx::Status<pdx::LocalChannelHandle> Duplicate(); + +private: + BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format, + uint64_t usage, size_t userMetadataSize); + + BufferHubBuffer(pdx::LocalChannelHandle mChannelHandle); + + int ImportGraphicBuffer(); + + // Global id for the buffer that is consistent across processes. + int mId = -1; + + // Client state mask of this BufferHubBuffer object. It is unique amoung all + // clients/users of the buffer. + uint32_t mClientStateMask = 0U; + + // Stores ground truth of the buffer. + AHardwareBuffer_Desc mBufferDesc; + + // Wrapps the gralloc buffer handle of this buffer. + dvr::NativeHandleWrapper<pdx::LocalHandle> mBufferHandle; + + // An ashmem-based metadata object. The same shared memory are mapped to the + // bufferhubd daemon and all buffer clients. + BufferHubMetadata mMetadata; + // Shortcuts to the atomics inside the header of mMetadata. + std::atomic<uint32_t>* buffer_state_ = nullptr; + std::atomic<uint32_t>* fence_state_ = nullptr; + std::atomic<uint32_t>* active_clients_bit_mask_ = nullptr; + + // PDX backend. + BufferHubClient mClient; +}; + +} // namespace android + +#endif // ANDROID_BUFFER_HUB_BUFFER_H_ diff --git a/libs/ui/include/ui/BufferHubDefs.h b/libs/ui/include/ui/BufferHubDefs.h new file mode 100644 index 0000000000..d259fefb8f --- /dev/null +++ b/libs/ui/include/ui/BufferHubDefs.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BUFFER_HUB_DEFS_H_ +#define ANDROID_BUFFER_HUB_DEFS_H_ + +#include <atomic> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpacked" +// TODO(b/118893702): remove dependency once DvrNativeBufferMetadata moved out of libdvr +#include <dvr/dvr_api.h> +#pragma clang diagnostic pop + +namespace android { + +namespace BufferHubDefs { + +// Single buffer clients (up to 16) ownership signal. +// 32-bit atomic unsigned int. +// Each client takes 2 bits. The first bit locates in the first 16 bits of +// buffer_state; the second bit locates in the last 16 bits of buffer_state. +// Client states: +// Gained state 11. Exclusive write state. +// Posted state 10. +// Acquired state 01. Shared read state. +// Released state 00. +// +// MSB LSB +// | | +// v v +// [C15|...|C1|C0|C15| ... |C1|C0] + +// Maximum number of clients a buffer can have. +static constexpr int kMaxNumberOfClients = 16; + +// Definition of bit masks. +// MSB LSB +// | kHighBitsMask | kLowbitsMask | +// v v v +// [b31| ... |b16|b15| ... |b0] + +// The location of lower 16 bits in the 32-bit buffer state. +static constexpr uint32_t kLowbitsMask = (1U << kMaxNumberOfClients) - 1U; + +// The location of higher 16 bits in the 32-bit buffer state. +static constexpr uint32_t kHighBitsMask = ~kLowbitsMask; + +// The client bit mask of the first client. +static constexpr uint32_t kFirstClientBitMask = (1U << kMaxNumberOfClients) + 1U; + +// Returns true if any of the client is in gained state. +static inline bool AnyClientGained(uint32_t state) { + uint32_t high_bits = state >> kMaxNumberOfClients; + uint32_t low_bits = state & kLowbitsMask; + return high_bits == low_bits && low_bits != 0U; +} + +// Returns true if the input client is in gained state. +static inline bool IsClientGained(uint32_t state, uint32_t client_bit_mask) { + return state == client_bit_mask; +} + +// Returns true if any of the client is in posted state. +static inline bool AnyClientPosted(uint32_t state) { + uint32_t high_bits = state >> kMaxNumberOfClients; + uint32_t low_bits = state & kLowbitsMask; + uint32_t posted_or_acquired = high_bits ^ low_bits; + return posted_or_acquired & high_bits; +} + +// Returns true if the input client is in posted state. +static inline bool IsClientPosted(uint32_t state, uint32_t client_bit_mask) { + uint32_t client_bits = state & client_bit_mask; + if (client_bits == 0U) return false; + uint32_t low_bits = client_bits & kLowbitsMask; + return low_bits == 0U; +} + +// Return true if any of the client is in acquired state. +static inline bool AnyClientAcquired(uint32_t state) { + uint32_t high_bits = state >> kMaxNumberOfClients; + uint32_t low_bits = state & kLowbitsMask; + uint32_t posted_or_acquired = high_bits ^ low_bits; + return posted_or_acquired & low_bits; +} + +// Return true if the input client is in acquired state. +static inline bool IsClientAcquired(uint32_t state, uint32_t client_bit_mask) { + uint32_t client_bits = state & client_bit_mask; + if (client_bits == 0U) return false; + uint32_t high_bits = client_bits & kHighBitsMask; + return high_bits == 0U; +} + +// Returns true if all clients are in released state. +static inline bool IsBufferReleased(uint32_t state) { + return state == 0U; +} + +// Returns true if the input client is in released state. +static inline bool IsClientReleased(uint32_t state, uint32_t client_bit_mask) { + return (state & client_bit_mask) == 0U; +} + +// Returns the next available buffer client's client_state_masks. +// @params union_bits. Union of all existing clients' client_state_masks. +static inline uint32_t FindNextAvailableClientStateMask(uint32_t union_bits) { + uint32_t low_union = union_bits & kLowbitsMask; + if (low_union == kLowbitsMask) return 0U; + uint32_t incremented = low_union + 1U; + uint32_t difference = incremented ^ low_union; + uint32_t new_low_bit = (difference + 1U) >> 1; + return new_low_bit + (new_low_bit << kMaxNumberOfClients); +} + +struct __attribute__((aligned(8))) MetadataHeader { + // Internal data format, which can be updated as long as the size, padding and field alignment + // of the struct is consistent within the same ABI. As this part is subject for future updates, + // it's not stable cross Android version, so don't have it visible from outside of the Android + // platform (include Apps and vendor HAL). + + // Every client takes up one bit from the higher 32 bits and one bit from the lower 32 bits in + // buffer_state. + std::atomic<uint32_t> buffer_state; + + // Every client takes up one bit in fence_state. Only the lower 32 bits are valid. The upper 32 + // bits are there for easier manipulation, but the value should be ignored. + std::atomic<uint32_t> fence_state; + + // Every client takes up one bit from the higher 32 bits and one bit from the lower 32 bits in + // active_clients_bit_mask. + std::atomic<uint32_t> active_clients_bit_mask; + + // Explicit padding 4 bytes. + uint32_t padding; + + // The index of the buffer queue where the buffer belongs to. + uint64_t queue_index; + + // Public data format, which should be updated with caution. See more details in dvr_api.h + DvrNativeBufferMetadata metadata; +}; + +static_assert(sizeof(MetadataHeader) == 128, "Unexpected MetadataHeader size"); +static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader); + +} // namespace BufferHubDefs + +} // namespace android + +#endif // ANDROID_BUFFER_HUB_DEFS_H_ diff --git a/libs/ui/include/ui/BufferHubEventFd.h b/libs/ui/include/ui/BufferHubEventFd.h new file mode 100644 index 0000000000..0e856bdb67 --- /dev/null +++ b/libs/ui/include/ui/BufferHubEventFd.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BUFFER_HUB_EVENT_FD_H_ +#define ANDROID_BUFFER_HUB_EVENT_FD_H_ + +#include <android-base/unique_fd.h> +#include <utils/Errors.h> + +namespace android { + +class BufferHubEventFd { +public: + /** + * Constructs a valid event fd. + */ + BufferHubEventFd(); + + /** + * Returns whether this BufferHubEventFd holds a valid event_fd. + */ + bool isValid() const { return get() >= 0; } + + /** + * Returns the fd number of the BufferHubEventFd object. Note that there is no ownership + * transfer. + */ + int get() const { return mFd.get(); } + + /** + * Signals the eventfd. + */ + status_t signal() const; + + /** + * Clears the signal from this eventfd if it is signaled. + */ + status_t clear() const; + +private: + base::unique_fd mFd; +}; + +} // namespace android + +#endif // ANDROID_BUFFER_HUB_EVENT_FD_H_ diff --git a/libs/ui/include/ui/BufferHubMetadata.h b/libs/ui/include/ui/BufferHubMetadata.h new file mode 100644 index 0000000000..212189497a --- /dev/null +++ b/libs/ui/include/ui/BufferHubMetadata.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BUFFER_HUB_METADATA_H_ +#define ANDROID_BUFFER_HUB_METADATA_H_ + +#include <android-base/unique_fd.h> +#include <ui/BufferHubDefs.h> + +namespace android { + +namespace { +using base::unique_fd; +} // namespace + +class BufferHubMetadata { +public: + // Creates a new BufferHubMetadata backed by an ashmem region. + // + // @param userMetadataSize Size in bytes of the user defined metadata. The entire metadata + // shared memory region to be allocated is the size of canonical + // BufferHubDefs::MetadataHeader plus userMetadataSize. + static BufferHubMetadata Create(size_t userMetadataSize); + + // Imports an existing BufferHubMetadata from an ashmem FD. + // + // @param ashmemFd Ashmem file descriptor representing an ashmem region. + static BufferHubMetadata Import(unique_fd ashmemFd); + + BufferHubMetadata() = default; + + BufferHubMetadata(BufferHubMetadata&& other) { *this = std::move(other); } + + ~BufferHubMetadata(); + + BufferHubMetadata& operator=(BufferHubMetadata&& other) { + if (this != &other) { + mUserMetadataSize = other.mUserMetadataSize; + other.mUserMetadataSize = 0; + + mAshmemFd = std::move(other.mAshmemFd); + + // The old raw mMetadataHeader pointer must be cleared, otherwise the destructor will + // automatically mummap() the shared memory. + mMetadataHeader = other.mMetadataHeader; + other.mMetadataHeader = nullptr; + } + return *this; + } + + // Returns true if the metadata is valid, i.e. the metadata has a valid ashmem fd and the ashmem + // has been mapped into virtual address space. + bool IsValid() const { return mAshmemFd.get() != -1 && mMetadataHeader != nullptr; } + + size_t user_metadata_size() const { return mUserMetadataSize; } + size_t metadata_size() const { return mUserMetadataSize + BufferHubDefs::kMetadataHeaderSize; } + + const unique_fd& ashmem_fd() const { return mAshmemFd; } + BufferHubDefs::MetadataHeader* metadata_header() { return mMetadataHeader; } + +private: + BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd, + BufferHubDefs::MetadataHeader* metadataHeader); + + BufferHubMetadata(const BufferHubMetadata&) = delete; + void operator=(const BufferHubMetadata&) = delete; + + size_t mUserMetadataSize = 0; + unique_fd mAshmemFd; + BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr; +}; + +} // namespace android + +#endif // ANDROID_BUFFER_HUB_METADATA_H_ diff --git a/libs/ui/include/ui/DetachedBufferHandle.h b/libs/ui/include/ui/DetachedBufferHandle.h deleted file mode 100644 index f3c328d52b..0000000000 --- a/libs/ui/include/ui/DetachedBufferHandle.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_DETACHED_BUFFER_HUB_HANDLE_H -#define ANDROID_DETACHED_BUFFER_HUB_HANDLE_H - -#include <pdx/channel_handle.h> - -#include <memory> - -namespace android { - -// A wrapper that holds a pdx::LocalChannelHandle object. From the handle, a BufferHub buffer can be -// created. Current implementation assumes that the underlying transport is using libpdx (thus -// holding a pdx::LocalChannelHandle object), but future implementation can change it to a Binder -// backend if ever needed. -class DetachedBufferHandle { -public: - static std::unique_ptr<DetachedBufferHandle> Create(pdx::LocalChannelHandle handle) { - return std::unique_ptr<DetachedBufferHandle>(new DetachedBufferHandle(std::move(handle))); - } - - // Accessors to get or take the internal pdx::LocalChannelHandle. - pdx::LocalChannelHandle& handle() { return mHandle; } - const pdx::LocalChannelHandle& handle() const { return mHandle; } - - // Returns whether the DetachedBufferHandle holds a BufferHub channel. - bool isValid() const { return mHandle.valid(); } - -private: - // Constructs a DetachedBufferHandle from a pdx::LocalChannelHandle. - explicit DetachedBufferHandle(pdx::LocalChannelHandle handle) : mHandle(std::move(handle)) {} - - pdx::LocalChannelHandle mHandle; -}; - -} // namespace android - -#endif // ANDROID_DETACHED_BUFFER_HUB_HANDLE_H diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h index 94caf6b9d3..8976d2d584 100644 --- a/libs/ui/include/ui/DisplayInfo.h +++ b/libs/ui/include/ui/DisplayInfo.h @@ -35,6 +35,8 @@ struct DisplayInfo { bool secure{false}; nsecs_t appVsyncOffset{0}; nsecs_t presentationDeadline{0}; + uint32_t viewportW{0}; + uint32_t viewportH{0}; }; /* Display orientations as defined in Surface.java and ISurfaceComposer.h. */ diff --git a/libs/ui/include/ui/DisplayedFrameStats.h b/libs/ui/include/ui/DisplayedFrameStats.h new file mode 100644 index 0000000000..7a70ea1f06 --- /dev/null +++ b/libs/ui/include/ui/DisplayedFrameStats.h @@ -0,0 +1,41 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <vector> + +namespace android { + +struct DisplayedFrameStats { + /* The number of frames represented by this sample. */ + uint64_t numFrames = 0; + /* A histogram counting how many times a pixel of a given value was displayed onscreen for + * FORMAT_COMPONENT_0. The buckets of the histogram are evenly weighted, the number of buckets + * is device specific. eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that + * 10 red pixels were displayed onscreen in range 0x00->0x3F, 6 red pixels + * were displayed onscreen in range 0x40->0x7F, etc. + */ + std::vector<uint64_t> component_0_sample = {}; + /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_1. */ + std::vector<uint64_t> component_1_sample = {}; + /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_2. */ + std::vector<uint64_t> component_2_sample = {}; + /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_3. */ + std::vector<uint64_t> component_3_sample = {}; +}; + +} // namespace android diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h index 871fcf2dfe..a5a1fcbde7 100644 --- a/libs/ui/include/ui/FenceTime.h +++ b/libs/ui/include/ui/FenceTime.h @@ -113,11 +113,6 @@ public: void signalForTest(nsecs_t signalTime); - // Override new and delete since this needs 8-byte alignment, which - // is not guaranteed on x86. - static void* operator new(size_t nbytes) noexcept; - static void operator delete(void *p); - private: // For tests only. If forceValidForTest is true, then getSignalTime will // never return SIGNAL_TIME_INVALID and isValid will always return true. diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h index 6a7479a68a..4cd9a0b236 100644 --- a/libs/ui/include/ui/FloatRect.h +++ b/libs/ui/include/ui/FloatRect.h @@ -28,7 +28,7 @@ public: float getHeight() const { return bottom - top; } FloatRect intersect(const FloatRect& other) const { - return { + FloatRect intersection = { // Inline to avoid tromping on other min/max defines or adding a // dependency on STL (left > other.left) ? left : other.left, @@ -36,6 +36,10 @@ public: (right < other.right) ? right : other.right, (bottom < other.bottom) ? bottom : other.bottom }; + if (intersection.getWidth() < 0 || intersection.getHeight() < 0) { + return {0, 0, 0, 0}; + } + return intersection; } float left = 0.0f; diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index 315db110a1..b73ca2b793 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -34,7 +34,10 @@ namespace android { -class DetachedBufferHandle; +#ifndef LIBUI_IN_VNDK +class BufferHubBuffer; +#endif // LIBUI_IN_VNDK + class GraphicBufferMapper; // =========================================================================== @@ -134,6 +137,11 @@ public: GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inUsage, std::string requestorName = "<Unknown>"); +#ifndef LIBUI_IN_VNDK + // Create a GraphicBuffer from an existing BufferHubBuffer. + GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer); +#endif // LIBUI_IN_VNDK + // return status status_t initCheck() const; @@ -145,6 +153,7 @@ public: uint32_t getLayerCount() const { return static_cast<uint32_t>(layerCount); } Rect getBounds() const { return Rect(width, height); } uint64_t getId() const { return mId; } + int32_t getBufferId() const { return mBufferId; } uint32_t getGenerationNumber() const { return mGenerationNumber; } void setGenerationNumber(uint32_t generation) { @@ -189,10 +198,10 @@ public: status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); - // Sets and takes DetachedBuffer. Should only be called from BufferHub. - bool isDetachedBuffer() const; - status_t setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> detachedBuffer); - std::unique_ptr<DetachedBufferHandle> takeDetachedBufferHandle(); +#ifndef LIBUI_IN_VNDK + // Returns whether this GraphicBuffer is backed by BufferHubBuffer. + bool isBufferHubBuffer() const; +#endif // LIBUI_IN_VNDK private: ~GraphicBuffer(); @@ -239,21 +248,21 @@ private: uint64_t mId; + // System unique buffer ID. Note that this is different from mId, which is process unique. For + // GraphicBuffer backed by BufferHub, the mBufferId is a system unique identifier that stays the + // same cross process for the same chunck of underlying memory. Also note that this only applies + // to GraphicBuffers that are backed by BufferHub. + int32_t mBufferId = -1; + // Stores the generation number of this buffer. If this number does not // match the BufferQueue's internal generation number (set through // IGBP::setGenerationNumber), attempts to attach the buffer will fail. uint32_t mGenerationNumber; - // Stores a BufferHub handle that can be used to re-attach this GraphicBuffer back into a - // BufferHub producer/consumer set. In terms of GraphicBuffer's relationship with BufferHub, - // there are three different modes: - // 1. Legacy mode: GraphicBuffer is not backed by BufferHub and mDetachedBufferHandle must be - // invalid. - // 2. Detached mode: GraphicBuffer is backed by BufferHub, but not part of a producer/consumer - // set. In this mode, mDetachedBufferHandle must be valid. - // 3. Attached mode: GraphicBuffer is backed by BufferHub and it's part of a producer/consumer - // set. In this mode, mDetachedBufferHandle must be invalid. - std::unique_ptr<DetachedBufferHandle> mDetachedBufferHandle; +#ifndef LIBUI_IN_VNDK + // Stores a BufferHubBuffer that handles buffer signaling, identification. + std::unique_ptr<BufferHubBuffer> mBufferHubBuffer; +#endif // LIBUI_IN_VNDK }; }; // namespace android diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h index 14a865e16c..7e2b230fe9 100644 --- a/libs/ui/include/ui/GraphicBufferAllocator.h +++ b/libs/ui/include/ui/GraphicBufferAllocator.h @@ -39,7 +39,6 @@ class Allocator; } class GraphicBufferMapper; -class String8; class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator> { @@ -53,7 +52,7 @@ public: status_t free(buffer_handle_t handle); - void dump(String8& res) const; + void dump(std::string& res) const; static void dumpToSystemLog(); private: diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h index 0fa819dce8..fb3c5f8eca 100644 --- a/libs/ui/include/ui/GraphicTypes.h +++ b/libs/ui/include/ui/GraphicTypes.h @@ -17,6 +17,7 @@ #pragma once #include <android/hardware/graphics/common/1.1/types.h> +#include <android/hardware/graphics/common/1.2/types.h> #include <system/graphics.h> // android::ui::* in this header file will alias different types as @@ -24,11 +25,11 @@ namespace android { namespace ui { -using android::hardware::graphics::common::V1_0::Hdr; -using android::hardware::graphics::common::V1_1::ColorMode; -using android::hardware::graphics::common::V1_1::Dataspace; using android::hardware::graphics::common::V1_1::PixelFormat; using android::hardware::graphics::common::V1_1::RenderIntent; +using android::hardware::graphics::common::V1_2::ColorMode; +using android::hardware::graphics::common::V1_2::Dataspace; +using android::hardware::graphics::common::V1_2::Hdr; } // namespace ui } // namespace android diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h index 0bec0b7f78..e9da087347 100644 --- a/libs/ui/include/ui/Rect.h +++ b/libs/ui/include/ui/Rect.h @@ -120,7 +120,7 @@ public: right = rb.x; bottom = rb.y; } - + // the following 4 functions return the 4 corners of the rect as Point Point leftTop() const { return Point(left, top); @@ -175,6 +175,11 @@ public: Rect& offsetTo(int32_t x, int32_t y); Rect& offsetBy(int32_t x, int32_t y); + /** + * Insets the rectangle on all sides specified by the insets. + */ + Rect& inset(int32_t _left, int32_t _top, int32_t _right, int32_t _bottom); + bool intersect(const Rect& with, Rect* result) const; // Create a new Rect by transforming this one using a graphics HAL diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h index 778845295f..79642ae032 100644 --- a/libs/ui/include/ui/Region.h +++ b/libs/ui/include/ui/Region.h @@ -25,12 +25,13 @@ #include <ui/Rect.h> #include <utils/Flattenable.h> -namespace android { -// --------------------------------------------------------------------------- +#include <android-base/macros.h> -class String8; +#include <string> +namespace android { // --------------------------------------------------------------------------- + class Region : public LightFlattenable<Region> { public: @@ -87,17 +88,19 @@ public: // these translate rhs first Region& translateSelf(int dx, int dy); + Region& scaleSelf(float sx, float sy); Region& orSelf(const Region& rhs, int dx, int dy); Region& xorSelf(const Region& rhs, int dx, int dy); Region& andSelf(const Region& rhs, int dx, int dy); Region& subtractSelf(const Region& rhs, int dx, int dy); + // these translate rhs first - const Region translate(int dx, int dy) const; - const Region merge(const Region& rhs, int dx, int dy) const; - const Region mergeExclusive(const Region& rhs, int dx, int dy) const; - const Region intersect(const Region& rhs, int dx, int dy) const; - const Region subtract(const Region& rhs, int dx, int dy) const; + const Region translate(int dx, int dy) const WARN_UNUSED; + const Region merge(const Region& rhs, int dx, int dy) const WARN_UNUSED; + const Region mergeExclusive(const Region& rhs, int dx, int dy) const WARN_UNUSED; + const Region intersect(const Region& rhs, int dx, int dy) const WARN_UNUSED; + const Region subtract(const Region& rhs, int dx, int dy) const WARN_UNUSED; // convenience operators overloads inline const Region operator | (const Region& rhs) const; @@ -140,8 +143,8 @@ public: status_t flatten(void* buffer, size_t size) const; status_t unflatten(void const* buffer, size_t size); - void dump(String8& out, const char* what, uint32_t flags=0) const; - void dump(const char* what, uint32_t flags=0) const; + void dump(std::string& out, const char* what, uint32_t flags=0) const; + void dump(const char* what, uint32_t flags=0) const; private: class rasterizer; diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h new file mode 100644 index 0000000000..900a5c46eb --- /dev/null +++ b/libs/ui/include/ui/Transform.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_TRANSFORM_H +#define ANDROID_TRANSFORM_H + +#include <stdint.h> +#include <sys/types.h> + +#include <hardware/hardware.h> +#include <math/vec2.h> +#include <math/vec3.h> +#include <ui/Point.h> +#include <ui/Rect.h> + +namespace android { + +class Region; + +namespace ui { + +class Transform { +public: + Transform(); + Transform(const Transform& other); + explicit Transform(uint32_t orientation); + ~Transform(); + + enum orientation_flags { + ROT_0 = 0x00000000, + FLIP_H = HAL_TRANSFORM_FLIP_H, + FLIP_V = HAL_TRANSFORM_FLIP_V, + ROT_90 = HAL_TRANSFORM_ROT_90, + ROT_180 = FLIP_H|FLIP_V, + ROT_270 = ROT_180|ROT_90, + ROT_INVALID = 0x80 + }; + + enum type_mask : uint32_t { + IDENTITY = 0, + TRANSLATE = 0x1, + ROTATE = 0x2, + SCALE = 0x4, + UNKNOWN = 0x8 + }; + + // query the transform + bool preserveRects() const; + uint32_t getType() const; + uint32_t getOrientation() const; + + const vec3& operator [] (size_t i) const; // returns column i + float tx() const; + float ty() const; + float sx() const; + float sy() const; + + // modify the transform + void reset(); + void set(float tx, float ty); + void set(float a, float b, float c, float d); + status_t set(uint32_t flags, float w, float h); + + // transform data + Rect makeBounds(int w, int h) const; + vec2 transform(int x, int y) const; + Region transform(const Region& reg) const; + Rect transform(const Rect& bounds, + bool roundOutwards = false) const; + FloatRect transform(const FloatRect& bounds) const; + Transform& operator = (const Transform& other); + Transform operator * (const Transform& rhs) const; + // assumes the last row is < 0 , 0 , 1 > + vec2 transform(const vec2& v) const; + vec3 transform(const vec3& v) const; + + Transform inverse() const; + + // for debugging + void dump(const char* name) const; + +private: + struct mat33 { + vec3 v[3]; + inline const vec3& operator [] (size_t i) const { return v[i]; } + inline vec3& operator [] (size_t i) { return v[i]; } + }; + + enum { UNKNOWN_TYPE = 0x80000000 }; + + uint32_t type() const; + static bool absIsOne(float f); + static bool isZero(float f); + + mat33 mMatrix; + mutable uint32_t mType; +}; + +} // namespace ui +} // namespace android + +#endif /* ANDROID_TRANSFORM_H */ diff --git a/libs/ui/include/ui/UiConfig.h b/libs/ui/include/ui/UiConfig.h index fcf8ed5d6b..d1d6014a7b 100644 --- a/libs/ui/include/ui/UiConfig.h +++ b/libs/ui/include/ui/UiConfig.h @@ -17,12 +17,12 @@ #ifndef ANDROID_UI_CONFIG_H #define ANDROID_UI_CONFIG_H -#include <utils/String8.h> +#include <string> namespace android { // Append the libui configuration details to configStr. -void appendUiConfigString(String8& configStr); +void appendUiConfigString(std::string& configStr); }; // namespace android diff --git a/libs/ui/include_vndk/ui/DisplayedFrameStats.h b/libs/ui/include_vndk/ui/DisplayedFrameStats.h new file mode 120000 index 0000000000..6014e1954a --- /dev/null +++ b/libs/ui/include_vndk/ui/DisplayedFrameStats.h @@ -0,0 +1 @@ +../../include/ui/DisplayedFrameStats.h
\ No newline at end of file diff --git a/libs/ui/include_vndk/ui/Transform.h b/libs/ui/include_vndk/ui/Transform.h new file mode 120000 index 0000000000..60633c2ef5 --- /dev/null +++ b/libs/ui/include_vndk/ui/Transform.h @@ -0,0 +1 @@ +../../include/ui/Transform.h
\ No newline at end of file diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index aef6428cc8..a670b3ce63 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -30,7 +30,44 @@ cc_test { cc_test { name: "GraphicBuffer_test", - shared_libs: ["libpdx_default_transport", "libui", "libutils"], + header_libs: [ + "libbufferhub_headers", + "libdvr_headers", + "libnativewindow_headers", + ], + shared_libs: [ + "libpdx_default_transport", + "libui", + "libutils", + ], srcs: ["GraphicBuffer_test.cpp"], cflags: ["-Wall", "-Werror"], } + +cc_test { + name: "BufferHub_test", + header_libs: [ + "libbufferhub_headers", + "libdvr_headers", + "libnativewindow_headers", + ], + static_libs: [ + "libgmock", + ], + shared_libs: [ + "android.frameworks.bufferhub@1.0", + "libcutils", + "libhidlbase", + "libhwbinder", + "liblog", + "libpdx_default_transport", + "libui", + "libutils" + ], + srcs: [ + "BufferHubBuffer_test.cpp", + "BufferHubEventFd_test.cpp", + "BufferHubMetadata_test.cpp", + ], + cflags: ["-Wall", "-Werror"], +} diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp new file mode 100644 index 0000000000..1b339a06d9 --- /dev/null +++ b/libs/ui/tests/BufferHubBuffer_test.cpp @@ -0,0 +1,344 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BufferHubBufferTest" + +#include <android/frameworks/bufferhub/1.0/IBufferClient.h> +#include <android/frameworks/bufferhub/1.0/IBufferHub.h> +#include <android/hardware_buffer.h> +#include <cutils/native_handle.h> +#include <gtest/gtest.h> +#include <hidl/ServiceManagement.h> +#include <hwbinder/IPCThreadState.h> +#include <ui/BufferHubBuffer.h> + +namespace android { + +namespace { + +const int kWidth = 640; +const int kHeight = 480; +const int kLayerCount = 1; +const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; +const int kUsage = 0; +const size_t kUserMetadataSize = 0; + +using BufferHubDefs::AnyClientAcquired; +using BufferHubDefs::AnyClientGained; +using BufferHubDefs::AnyClientPosted; +using BufferHubDefs::IsBufferReleased; +using BufferHubDefs::IsClientAcquired; +using BufferHubDefs::IsClientGained; +using BufferHubDefs::IsClientPosted; +using BufferHubDefs::IsClientReleased; +using BufferHubDefs::kFirstClientBitMask; +using BufferHubDefs::kMetadataHeaderSize; +using frameworks::bufferhub::V1_0::BufferHubStatus; +using frameworks::bufferhub::V1_0::IBufferClient; +using frameworks::bufferhub::V1_0::IBufferHub; +using hardware::hidl_handle; +using hardware::graphics::common::V1_2::HardwareBufferDescription; +using hidl::base::V1_0::IBase; +using pdx::LocalChannelHandle; + +class BufferHubBufferTest : public ::testing::Test { +protected: + void SetUp() override { android::hardware::ProcessState::self()->startThreadPool(); } +}; + +class BufferHubBufferStateTransitionTest : public BufferHubBufferTest { +protected: + void SetUp() override { + BufferHubBufferTest::SetUp(); + CreateTwoClientsOfABuffer(); + } + + std::unique_ptr<BufferHubBuffer> b1; + uint64_t b1ClientMask = 0U; + std::unique_ptr<BufferHubBuffer> b2; + uint64_t b2ClientMask = 0U; + +private: + // Creates b1 and b2 as the clients of the same buffer for testing. + void CreateTwoClientsOfABuffer(); +}; + +void BufferHubBufferStateTransitionTest::CreateTwoClientsOfABuffer() { + b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); + b1ClientMask = b1->client_state_mask(); + ASSERT_NE(b1ClientMask, 0U); + auto statusOrHandle = b1->Duplicate(); + ASSERT_TRUE(statusOrHandle); + LocalChannelHandle h2 = statusOrHandle.take(); + b2 = BufferHubBuffer::Import(std::move(h2)); + b2ClientMask = b2->client_state_mask(); + ASSERT_NE(b2ClientMask, 0U); + ASSERT_NE(b2ClientMask, b1ClientMask); +} + +TEST_F(BufferHubBufferTest, CreateBufferHubBufferFails) { + // Buffer Creation will fail: BLOB format requires height to be 1. + auto b1 = BufferHubBuffer::Create(kWidth, /*height=*/2, kLayerCount, + /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, kUserMetadataSize); + + EXPECT_FALSE(b1->IsConnected()); + EXPECT_FALSE(b1->IsValid()); + + // Buffer Creation will fail: user metadata size too large. + auto b2 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + /*userMetadataSize=*/std::numeric_limits<size_t>::max()); + + EXPECT_FALSE(b2->IsConnected()); + EXPECT_FALSE(b2->IsValid()); + + // Buffer Creation will fail: user metadata size too large. + const size_t userMetadataSize = std::numeric_limits<size_t>::max() - kMetadataHeaderSize; + auto b3 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + userMetadataSize); + + EXPECT_FALSE(b3->IsConnected()); + EXPECT_FALSE(b3->IsValid()); +} + +TEST_F(BufferHubBufferTest, CreateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + EXPECT_TRUE(b1->IsConnected()); + EXPECT_TRUE(b1->IsValid()); + EXPECT_NE(b1->id(), 0); +} + +TEST_F(BufferHubBufferTest, DuplicateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + int id1 = b1->id(); + uint64_t bufferStateMask1 = b1->client_state_mask(); + EXPECT_NE(bufferStateMask1, 0U); + EXPECT_TRUE(b1->IsValid()); + EXPECT_EQ(b1->user_metadata_size(), kUserMetadataSize); + + auto statusOrHandle = b1->Duplicate(); + EXPECT_TRUE(statusOrHandle); + + // The detached buffer should still be valid. + EXPECT_TRUE(b1->IsConnected()); + EXPECT_TRUE(b1->IsValid()); + + // Gets the channel handle for the duplicated buffer. + LocalChannelHandle h2 = statusOrHandle.take(); + EXPECT_TRUE(h2.valid()); + + std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(std::move(h2)); + EXPECT_FALSE(h2.valid()); + ASSERT_TRUE(b2 != nullptr); + EXPECT_TRUE(b2->IsValid()); + EXPECT_EQ(b2->user_metadata_size(), kUserMetadataSize); + + int id2 = b2->id(); + uint64_t bufferStateMask2 = b2->client_state_mask(); + EXPECT_NE(bufferStateMask2, 0U); + + // These two buffer instances are based on the same physical buffer under the + // hood, so they should share the same id. + EXPECT_EQ(id1, id2); + // We use client_state_mask() to tell those two instances apart. + EXPECT_NE(bufferStateMask1, bufferStateMask2); + + // Both buffer instances should be in released state currently. + EXPECT_TRUE(IsBufferReleased(b1->buffer_state())); + EXPECT_TRUE(IsBufferReleased(b2->buffer_state())); + + // TODO(b/112338294): rewrite test after migration + return; +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + // Successful gaining the buffer should change the buffer state bit of b1 to + // gained state, other client state bits to released state. + EXPECT_EQ(b1->Gain(), 0); + EXPECT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask)); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromGainedState) { + ASSERT_EQ(b1->Gain(), 0); + auto current_buffer_state = b1->buffer_state(); + ASSERT_TRUE(IsClientGained(current_buffer_state, b1ClientMask)); + + // Gaining from gained state by the same client should not return error. + EXPECT_EQ(b1->Gain(), 0); + + // Gaining from gained state by another client should return error. + EXPECT_EQ(b2->Gain(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + ASSERT_TRUE(AnyClientAcquired(b1->buffer_state())); + + // Gaining from acquired state should fail. + EXPECT_EQ(b1->Gain(), -EBUSY); + EXPECT_EQ(b2->Gain(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromOtherClientInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + // Gaining a buffer who has other posted client should succeed. + EXPECT_EQ(b1->Gain(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + // A posted client should be able to gain the buffer when there is no other clients in + // acquired state. + EXPECT_EQ(b2->Gain(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromOtherInGainedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask)); + + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromSelfInGainedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask)); + + EXPECT_EQ(b1->Post(), 0); + auto current_buffer_state = b1->buffer_state(); + EXPECT_TRUE(IsClientReleased(current_buffer_state, b1ClientMask)); + EXPECT_TRUE(IsClientPosted(current_buffer_state, b2ClientMask)); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + // Post from posted state should fail. + EXPECT_EQ(b1->Post(), -EBUSY); + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + ASSERT_TRUE(AnyClientAcquired(b1->buffer_state())); + + // Posting from acquired state should fail. + EXPECT_EQ(b1->Post(), -EBUSY); + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + // Posting from released state should fail. + EXPECT_EQ(b1->Post(), -EBUSY); + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask)); + + // Acquire from posted state should pass. + EXPECT_EQ(b2->Acquire(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromOtherInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask)); + + // Acquire from released state should fail, although there are other clients + // in posted state. + EXPECT_EQ(b1->Acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + auto current_buffer_state = b1->buffer_state(); + ASSERT_TRUE(IsClientAcquired(current_buffer_state, b2ClientMask)); + + // Acquiring from acquired state by the same client should not error out. + EXPECT_EQ(b2->Acquire(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + // Acquiring form released state should fail. + EXPECT_EQ(b1->Acquire(), -EBUSY); + EXPECT_EQ(b2->Acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromGainedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(AnyClientGained(b1->buffer_state())); + + // Acquiring from gained state should fail. + EXPECT_EQ(b1->Acquire(), -EBUSY); + EXPECT_EQ(b2->Acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + EXPECT_EQ(b1->Release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(AnyClientGained(b1->buffer_state())); + + EXPECT_EQ(b1->Release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + EXPECT_EQ(b2->Release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInAcquiredState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_EQ(b2->Acquire(), 0); + ASSERT_TRUE(AnyClientAcquired(b1->buffer_state())); + + EXPECT_EQ(b2->Release(), 0); +} + +} // namespace +} // namespace android diff --git a/libs/ui/tests/BufferHubEventFd_test.cpp b/libs/ui/tests/BufferHubEventFd_test.cpp new file mode 100644 index 0000000000..92fb33ff48 --- /dev/null +++ b/libs/ui/tests/BufferHubEventFd_test.cpp @@ -0,0 +1,338 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BufferHubEventFdTest" + +#include <sys/epoll.h> +#include <sys/eventfd.h> + +#include <array> +#include <condition_variable> +#include <mutex> +#include <thread> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <ui/BufferHubEventFd.h> + +namespace android { + +namespace { + +const int kTimeout = 100; +const std::chrono::milliseconds kTimeoutMs(kTimeout); + +using ::testing::Contains; +using BufferHubEventFdTest = ::testing::Test; + +} // namespace + +TEST_F(BufferHubEventFdTest, EventFd_testSingleEpollFd) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + + base::unique_fd epollFd(epoll_create(64)); + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + + ASSERT_GE(epollFd.get(), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + std::array<epoll_event, 1> events; + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + eventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + + // The epoll fd is edge triggered, so it only responds to the eventFd once. + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); +} + +TEST_F(BufferHubEventFdTest, EventFd_testClear) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + + base::unique_fd epollFd(epoll_create(64)); + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + + ASSERT_GE(epollFd.get(), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + eventFd.signal(); + eventFd.clear(); + + std::array<epoll_event, 1> events; + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); +} + +TEST_F(BufferHubEventFdTest, EventFd_testDupEventFd) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + + base::unique_fd epollFd(epoll_create(64)); + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + + ASSERT_GE(epollFd.get(), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + // Technically, the dupliated eventFd and the original eventFd are pointing + // to the same kernel object. This test signals the duplicated eventFd but epolls the origianl + // eventFd. + base::unique_fd dupedEventFd(dup(eventFd.get())); + ASSERT_GE(dupedEventFd.get(), 0); + + std::array<epoll_event, 1> events; + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + eventfd_write(dupedEventFd.get(), 1); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + + // The epoll fd is edge triggered, so it only responds to the eventFd once. + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + eventfd_write(dupedEventFd.get(), 1); + + eventfd_t value; + eventfd_read(dupedEventFd.get(), &value); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); +} + +TEST_F(BufferHubEventFdTest, EventFd_testTwoEpollFds) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + + base::unique_fd epollFd1(epoll_create(64)); + base::unique_fd epollFd2(epoll_create(64)); + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + + ASSERT_GE(epollFd1.get(), 0); + ASSERT_GE(epollFd2.get(), 0); + + // Register the same eventFd to two EpollFds. + ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + std::array<epoll_event, 1> events; + EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); + EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); + + eventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1); + EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 1); + + // The epoll fd is edge triggered, so it only responds to the eventFd once. + EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); + EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); + + eventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1); + + eventFd.clear(); + EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); + EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); +} + +TEST_F(BufferHubEventFdTest, EventFd_testTwoEventFds) { + BufferHubEventFd eventFd1; + BufferHubEventFd eventFd2; + + ASSERT_TRUE(eventFd1.isValid()); + ASSERT_TRUE(eventFd2.isValid()); + + base::unique_fd epollFd(epoll_create(64)); + epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}}; + epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}}; + + ASSERT_GE(epollFd.get(), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0); + + std::array<epoll_event, 2> events; + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + // Signal one by one. + eventFd1.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + EXPECT_EQ(events[0].data.u32, e1.data.u32); + + eventFd2.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + EXPECT_EQ(events[0].data.u32, e2.data.u32); + + // Signal both. + eventFd1.signal(); + eventFd2.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 2); + + uint32_t u32s[] = {events[0].data.u32, events[1].data.u32}; + EXPECT_THAT(u32s, Contains(e1.data.u32)); + EXPECT_THAT(u32s, Contains(e2.data.u32)); + + // The epoll fd is edge triggered, so it only responds to the eventFd once. + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + eventFd1.signal(); + eventFd2.signal(); + eventFd2.clear(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); +} + +TEST_F(BufferHubEventFdTest, EventFd_testPollingThreadWithTwoEventFds) { + BufferHubEventFd eventFd1; + BufferHubEventFd eventFd2; + + ASSERT_TRUE(eventFd1.isValid()); + ASSERT_TRUE(eventFd2.isValid()); + + base::unique_fd epollFd(epoll_create(64)); + epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}}; + epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}}; + + ASSERT_GE(epollFd.get(), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0); + + int countEvent1 = 0; + int countEvent2 = 0; + std::atomic<bool> stop{false}; + std::mutex mx; + std::condition_variable cv; + + std::thread pollingThread([&] { + std::array<epoll_event, 2> events; + while (true) { + if (stop.load()) { + break; + } + int ret = epoll_wait(epollFd.get(), events.data(), events.size(), kTimeout); + ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed."); + + std::lock_guard<std::mutex> lock(mx); + for (int i = 0; i < ret; i++) { + if (events[i].data.u32 == e1.data.u32) { + countEvent1++; + cv.notify_one(); + } else if (events[i].data.u32 == e2.data.u32) { + countEvent2++; + cv.notify_one(); + } + } + } + }); + + { + std::unique_lock<std::mutex> lock(mx); + + eventFd1.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 1; })); + + eventFd1.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 2; })); + + eventFd2.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 1; })); + + eventFd1.clear(); + eventFd2.clear(); + EXPECT_EQ(countEvent1, 2); + EXPECT_EQ(countEvent2, 1); + + eventFd1.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 3; })); + + eventFd2.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 2; })); + } + + stop.store(true); + pollingThread.join(); +} + +TEST_F(BufferHubEventFdTest, EventFd_testTwoPollingThreads) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + + base::unique_fd epollFd1(epoll_create(64)); + base::unique_fd epollFd2(epoll_create(64)); + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + + ASSERT_GE(epollFd1.get(), 0); + ASSERT_GE(epollFd2.get(), 0); + + // Register the same eventFd to two EpollFds. + ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + int countEpoll1 = 0; + int countEpoll2 = 0; + std::atomic<bool> stop{false}; + std::mutex mx; + std::condition_variable cv; + + std::thread pollingThread1([&] { + std::array<epoll_event, 1> events; + while (!stop.load()) { + int ret = epoll_wait(epollFd1.get(), events.data(), events.size(), kTimeout); + ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed."); + + if (ret > 0) { + std::lock_guard<std::mutex> lock(mx); + countEpoll1++; + cv.notify_one(); + } + } + }); + + std::thread pollingThread2([&] { + std::array<epoll_event, 1> events; + while (!stop.load()) { + int ret = epoll_wait(epollFd2.get(), events.data(), events.size(), kTimeout); + ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed."); + + if (ret > 0) { + std::lock_guard<std::mutex> lock(mx); + countEpoll2++; + cv.notify_one(); + } + } + }); + + { + std::unique_lock<std::mutex> lock(mx); + + eventFd.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 1; })); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 1; })); + + eventFd.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 2; })); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 2; })); + + eventFd.clear(); + EXPECT_EQ(countEpoll1, 2); + EXPECT_EQ(countEpoll2, 2); + + eventFd.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll1 == 3; })); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEpoll2 == 3; })); + } + + stop.store(true); + pollingThread1.join(); + pollingThread2.join(); +} + +} // namespace android diff --git a/libs/ui/tests/BufferHubMetadata_test.cpp b/libs/ui/tests/BufferHubMetadata_test.cpp new file mode 100644 index 0000000000..11f8e57adc --- /dev/null +++ b/libs/ui/tests/BufferHubMetadata_test.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <ui/BufferHubMetadata.h> + +using android::BufferHubDefs::IsBufferReleased; + +namespace android { +namespace dvr { + +constexpr size_t kEmptyUserMetadataSize = 0; + +class BufferHubMetadataTest : public ::testing::Test {}; + +TEST_F(BufferHubMetadataTest, Create_UserMetdataSizeTooBig) { + BufferHubMetadata m1 = + BufferHubMetadata::Create(std::numeric_limits<uint32_t>::max()); + EXPECT_FALSE(m1.IsValid()); +} + +TEST_F(BufferHubMetadataTest, Create_Success) { + BufferHubMetadata m1 = BufferHubMetadata::Create(kEmptyUserMetadataSize); + EXPECT_TRUE(m1.IsValid()); + EXPECT_NE(m1.metadata_header(), nullptr); +} + +TEST_F(BufferHubMetadataTest, Import_Success) { + BufferHubMetadata m1 = BufferHubMetadata::Create(kEmptyUserMetadataSize); + EXPECT_TRUE(m1.IsValid()); + EXPECT_NE(m1.metadata_header(), nullptr); + + unique_fd h2 = unique_fd(dup(m1.ashmem_fd().get())); + EXPECT_NE(h2.get(), -1); + + BufferHubMetadata m2 = BufferHubMetadata::Import(std::move(h2)); + EXPECT_EQ(h2.get(), -1); + EXPECT_TRUE(m1.IsValid()); + BufferHubDefs::MetadataHeader* mh1 = m1.metadata_header(); + EXPECT_NE(mh1, nullptr); + + EXPECT_TRUE(IsBufferReleased(mh1->buffer_state.load())); + + EXPECT_TRUE(m2.IsValid()); + BufferHubDefs::MetadataHeader* mh2 = m2.metadata_header(); + EXPECT_NE(mh2, nullptr); + + EXPECT_TRUE(IsBufferReleased(mh2->buffer_state.load())); +} + +TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) { + BufferHubMetadata m1 = BufferHubMetadata::Create(sizeof(int)); + EXPECT_TRUE(m1.IsValid()); + EXPECT_NE(m1.metadata_header(), nullptr); + EXPECT_NE(m1.ashmem_fd().get(), -1); + EXPECT_EQ(m1.user_metadata_size(), sizeof(int)); + + BufferHubMetadata m2 = std::move(m1); + + // After the move, the metadata header (a raw pointer) should be reset in the older buffer. + EXPECT_EQ(m1.metadata_header(), nullptr); + EXPECT_NE(m2.metadata_header(), nullptr); + + EXPECT_EQ(m1.ashmem_fd().get(), -1); + EXPECT_NE(m2.ashmem_fd().get(), -1); + + EXPECT_EQ(m1.user_metadata_size(), 0U); + EXPECT_EQ(m2.user_metadata_size(), sizeof(int)); + + BufferHubMetadata m3{std::move(m2)}; + + // After the move, the metadata header (a raw pointer) should be reset in the older buffer. + EXPECT_EQ(m2.metadata_header(), nullptr); + EXPECT_NE(m3.metadata_header(), nullptr); + + EXPECT_EQ(m2.ashmem_fd().get(), -1); + EXPECT_NE(m3.ashmem_fd().get(), -1); + + EXPECT_EQ(m2.user_metadata_size(), 0U); + EXPECT_EQ(m3.user_metadata_size(), sizeof(int)); +} + +} // namespace dvr +} // namespace android diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp index eb679ac236..81ab3acfe4 100644 --- a/libs/ui/tests/GraphicBuffer_test.cpp +++ b/libs/ui/tests/GraphicBuffer_test.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "GraphicBufferTest" -#include <ui/DetachedBufferHandle.h> +#include <ui/BufferHubBuffer.h> #include <ui/GraphicBuffer.h> #include <gtest/gtest.h> @@ -35,30 +35,43 @@ constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN; class GraphicBufferTest : public testing::Test {}; -TEST_F(GraphicBufferTest, DetachedBuffer) { - sp<GraphicBuffer> buffer( - new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount, kTestUsage)); +TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) { + std::unique_ptr<BufferHubBuffer> b1 = + BufferHubBuffer::Create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat, + kTestUsage, /*userMetadataSize=*/0); + EXPECT_NE(b1, nullptr); + EXPECT_TRUE(b1->IsValid()); + + sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1))); + EXPECT_TRUE(gb->isBufferHubBuffer()); - // Currently a newly allocated GraphicBuffer is in legacy mode, i.e. not associated with - // BufferHub. But this may change in the future. - EXPECT_FALSE(buffer->isDetachedBuffer()); + EXPECT_EQ(gb->getWidth(), kTestWidth); + EXPECT_EQ(gb->getHeight(), kTestHeight); + EXPECT_EQ(static_cast<uint32_t>(gb->getPixelFormat()), kTestFormat); + EXPECT_EQ(gb->getUsage(), kTestUsage); + EXPECT_EQ(gb->getLayerCount(), kTestLayerCount); +} - pdx::LocalChannelHandle channel{nullptr, 1234}; - EXPECT_TRUE(channel.valid()); +TEST_F(GraphicBufferTest, InvalidBufferIdForNoneBufferHubBuffer) { + sp<GraphicBuffer> gb( + new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount, kTestUsage)); + EXPECT_FALSE(gb->isBufferHubBuffer()); + EXPECT_EQ(gb->getBufferId(), -1); +} - std::unique_ptr<DetachedBufferHandle> handle = DetachedBufferHandle::Create(std::move(channel)); - EXPECT_FALSE(channel.valid()); - EXPECT_TRUE(handle->isValid()); - EXPECT_TRUE(handle->handle().valid()); +TEST_F(GraphicBufferTest, BufferIdMatchesBufferHubBufferId) { + std::unique_ptr<BufferHubBuffer> b1 = + BufferHubBuffer::Create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat, + kTestUsage, /*userMetadataSize=*/0); + EXPECT_NE(b1, nullptr); + EXPECT_TRUE(b1->IsValid()); - buffer->setDetachedBufferHandle(std::move(handle)); - EXPECT_TRUE(handle == nullptr); - EXPECT_TRUE(buffer->isDetachedBuffer()); + int b1_id = b1->id(); + EXPECT_GE(b1_id, 0); - handle = buffer->takeDetachedBufferHandle(); - EXPECT_TRUE(handle != nullptr); - EXPECT_TRUE(handle->isValid()); - EXPECT_FALSE(buffer->isDetachedBuffer()); + sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1))); + EXPECT_TRUE(gb->isBufferHubBuffer()); + EXPECT_EQ(gb->getBufferId(), b1_id); } } // namespace android diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp index 69b6422d1d..fa928308cf 100644 --- a/libs/vr/libbufferhub/Android.bp +++ b/libs/vr/libbufferhub/Android.bp @@ -12,20 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. +cc_library_headers { + name: "libbufferhub_headers", + export_include_dirs: ["include"], + vendor_available: true, // TODO(b/112338314): Does shouldn't be available to vendor. +} + sourceFiles = [ - "buffer_hub_client.cpp", + "buffer_hub_base.cpp", "buffer_hub_rpc.cpp", - "detached_buffer.cpp", + "consumer_buffer.cpp", "ion_buffer.cpp", -] - -localIncludeFiles = [ - "include", + "producer_buffer.cpp", ] sharedLibraries = [ "libbase", - "libbinder", "libcutils", "libhardware", "liblog", @@ -36,6 +38,7 @@ sharedLibraries = [ ] headerLibraries = [ + "libbufferhub_headers", "libdvr_headers", "libnativebase_headers", ] @@ -49,13 +52,16 @@ cc_library { "-Wall", "-Werror", ], - export_include_dirs: localIncludeFiles, shared_libs: sharedLibraries, header_libs: headerLibraries, name: "libbufferhub", export_header_lib_headers: [ + "libbufferhub_headers", "libnativebase_headers", ], + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, } cc_test { @@ -64,5 +70,7 @@ cc_test { shared_libs: sharedLibraries, header_libs: headerLibraries, name: "buffer_hub-test", -} + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp index e24739845d..1359f4c137 100644 --- a/libs/vr/libbufferhub/buffer_hub-test.cpp +++ b/libs/vr/libbufferhub/buffer_hub-test.cpp @@ -1,11 +1,12 @@ #include <gtest/gtest.h> #include <poll.h> -#include <private/dvr/buffer_hub_client.h> #include <private/dvr/bufferhub_rpc.h> -#include <private/dvr/detached_buffer.h> +#include <private/dvr/consumer_buffer.h> +#include <private/dvr/producer_buffer.h> #include <sys/epoll.h> #include <sys/eventfd.h> -#include <ui/DetachedBufferHandle.h> +#include <ui/BufferHubBuffer.h> +#include <ui/BufferHubDefs.h> #include <mutex> #include <thread> @@ -19,18 +20,20 @@ return result; \ })() +using android::BufferHubBuffer; using android::GraphicBuffer; using android::sp; -using android::dvr::BufferConsumer; -using android::dvr::BufferProducer; -using android::dvr::DetachedBuffer; -using android::dvr::BufferHubDefs::IsBufferAcquired; -using android::dvr::BufferHubDefs::IsBufferGained; -using android::dvr::BufferHubDefs::IsBufferPosted; -using android::dvr::BufferHubDefs::IsBufferReleased; -using android::dvr::BufferHubDefs::kConsumerStateMask; -using android::dvr::BufferHubDefs::kMetadataHeaderSize; -using android::dvr::BufferHubDefs::kProducerStateBit; +using android::BufferHubDefs::AnyClientAcquired; +using android::BufferHubDefs::AnyClientGained; +using android::BufferHubDefs::AnyClientPosted; +using android::BufferHubDefs::IsBufferReleased; +using android::BufferHubDefs::IsClientAcquired; +using android::BufferHubDefs::IsClientPosted; +using android::BufferHubDefs::IsClientReleased; +using android::BufferHubDefs::kFirstClientBitMask; +using android::BufferHubDefs::kMetadataHeaderSize; +using android::dvr::ConsumerBuffer; +using android::dvr::ProducerBuffer; using android::pdx::LocalChannelHandle; using android::pdx::LocalHandle; using android::pdx::Status; @@ -41,80 +44,70 @@ const int kLayerCount = 1; const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; const int kUsage = 0; const size_t kUserMetadataSize = 0; -const uint64_t kContext = 42; -const size_t kMaxConsumerCount = 63; +// Maximum number of consumers for the buffer that only has one producer in the +// test. +const size_t kMaxConsumerCount = + android::BufferHubDefs::kMaxNumberOfClients - 1; const int kPollTimeoutMs = 100; using LibBufferHubTest = ::testing::Test; TEST_F(LibBufferHubTest, TestBasicUsage) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); - ASSERT_TRUE(c.get() != nullptr); + std::unique_ptr<ConsumerBuffer> c1 = + ConsumerBuffer::Import(p->CreateConsumer()); + ASSERT_TRUE(c1.get() != nullptr); // Check that consumers can spawn other consumers. - std::unique_ptr<BufferConsumer> c2 = - BufferConsumer::Import(c->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c2 = + ConsumerBuffer::Import(c1->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); - // Producer state mask is unique, i.e. 1. - EXPECT_EQ(p->buffer_state_bit(), kProducerStateBit); - // Consumer state mask cannot have producer bit on. - EXPECT_EQ(c->buffer_state_bit() & kProducerStateBit, 0U); - // Consumer state mask must be a single, i.e. power of 2. - EXPECT_NE(c->buffer_state_bit(), 0U); - EXPECT_EQ(c->buffer_state_bit() & (c->buffer_state_bit() - 1), 0U); - // Consumer state mask cannot have producer bit on. - EXPECT_EQ(c2->buffer_state_bit() & kProducerStateBit, 0U); - // Consumer state mask must be a single, i.e. power of 2. - EXPECT_NE(c2->buffer_state_bit(), 0U); - EXPECT_EQ(c2->buffer_state_bit() & (c2->buffer_state_bit() - 1), 0U); - // Each consumer should have unique bit. - EXPECT_EQ(c->buffer_state_bit() & c2->buffer_state_bit(), 0U); + // Checks the state masks of client p, c1 and c2. + EXPECT_EQ(p->client_state_mask(), kFirstClientBitMask); + EXPECT_EQ(c1->client_state_mask(), kFirstClientBitMask << 1); + EXPECT_EQ(c2->client_state_mask(), kFirstClientBitMask << 2); // Initial state: producer not available, consumers not available. EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); - EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); + EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs))); EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - EXPECT_EQ(0, p->Post(LocalHandle(), kContext)); + EXPECT_EQ(0, p->GainAsync()); + EXPECT_EQ(0, p->Post(LocalHandle())); // New state: producer not available, consumers available. EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); - EXPECT_EQ(1, RETRY_EINTR(c->Poll(kPollTimeoutMs))); + EXPECT_EQ(1, RETRY_EINTR(c1->Poll(kPollTimeoutMs))); EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - uint64_t context; LocalHandle fence; - EXPECT_EQ(0, c->Acquire(&fence, &context)); - EXPECT_EQ(kContext, context); - EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); + EXPECT_EQ(0, c1->Acquire(&fence)); + EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs))); EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - EXPECT_EQ(0, c2->Acquire(&fence, &context)); - EXPECT_EQ(kContext, context); + EXPECT_EQ(0, c2->Acquire(&fence)); EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); + EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs))); - EXPECT_EQ(0, c->Release(LocalHandle())); + EXPECT_EQ(0, c1->Release(LocalHandle())); EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); EXPECT_EQ(0, c2->Discard()); - EXPECT_EQ(1, RETRY_EINTR(p->Poll(kPollTimeoutMs))); + EXPECT_EQ(0, p->Gain(&fence)); EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); - EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); + EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs))); EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); } TEST_F(LibBufferHubTest, TestEpoll) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); LocalHandle epoll_fd{epoll_create1(EPOLL_CLOEXEC)}; @@ -146,8 +139,9 @@ TEST_F(LibBufferHubTest, TestEpoll) { // No events should be signaled initially. ASSERT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 0)); - // Post the producer and check for consumer signal. - EXPECT_EQ(0, p->Post({}, kContext)); + // Gain and post the producer and check for consumer signal. + EXPECT_EQ(0, p->GainAsync()); + EXPECT_EQ(0, p->Post({})); ASSERT_EQ(1, epoll_wait(epoll_fd.Get(), events.data(), events.size(), kPollTimeoutMs)); ASSERT_TRUE(events[0].events & EPOLLIN); @@ -177,21 +171,21 @@ TEST_F(LibBufferHubTest, TestEpoll) { } TEST_F(LibBufferHubTest, TestStateMask) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); // It's ok to create up to kMaxConsumerCount consumer buffers. - uint64_t buffer_state_bits = p->buffer_state_bit(); - std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs; + uint32_t client_state_masks = p->client_state_mask(); + std::array<std::unique_ptr<ConsumerBuffer>, kMaxConsumerCount> cs; for (size_t i = 0; i < kMaxConsumerCount; i++) { - cs[i] = BufferConsumer::Import(p->CreateConsumer()); + cs[i] = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(cs[i].get() != nullptr); // Expect all buffers have unique state mask. - EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0U); - buffer_state_bits |= cs[i]->buffer_state_bit(); + EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U); + client_state_masks |= cs[i]->client_state_mask(); } - EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask); + EXPECT_EQ(client_state_masks, ~0U); // The 64th creation will fail with out-of-memory error. auto state = p->CreateConsumer(); @@ -199,97 +193,84 @@ TEST_F(LibBufferHubTest, TestStateMask) { // Release any consumer should allow us to re-create. for (size_t i = 0; i < kMaxConsumerCount; i++) { - buffer_state_bits &= ~cs[i]->buffer_state_bit(); + client_state_masks &= ~cs[i]->client_state_mask(); cs[i] = nullptr; - cs[i] = BufferConsumer::Import(p->CreateConsumer()); + cs[i] = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(cs[i].get() != nullptr); // The released state mask will be reused. - EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0U); - buffer_state_bits |= cs[i]->buffer_state_bit(); - EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask); + EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U); + client_state_masks |= cs[i]->client_state_mask(); } } TEST_F(LibBufferHubTest, TestStateTransitions) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); - uint64_t context; LocalHandle fence; + EXPECT_EQ(0, p->GainAsync()); - // The producer buffer starts in gained state. - - // Acquire, release, and gain in gained state should fail. - EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); - EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); - EXPECT_EQ(-EALREADY, p->Gain(&fence)); + // Acquire in gained state should fail. + EXPECT_EQ(-EBUSY, c->Acquire(&fence)); // Post in gained state should succeed. - EXPECT_EQ(0, p->Post(LocalHandle(), kContext)); + EXPECT_EQ(0, p->Post(LocalHandle())); - // Post, release, and gain in posted state should fail. - EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext)); - EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); + // Post and gain in posted state should fail. + EXPECT_EQ(-EBUSY, p->Post(LocalHandle())); EXPECT_EQ(-EBUSY, p->Gain(&fence)); // Acquire in posted state should succeed. - EXPECT_LE(0, c->Acquire(&fence, &context)); + EXPECT_EQ(0, c->Acquire(&fence)); // Acquire, post, and gain in acquired state should fail. - EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); - EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext)); + EXPECT_EQ(-EBUSY, c->Acquire(&fence)); + EXPECT_EQ(-EBUSY, p->Post(LocalHandle())); EXPECT_EQ(-EBUSY, p->Gain(&fence)); // Release in acquired state should succeed. EXPECT_EQ(0, c->Release(LocalHandle())); EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); - // Release, acquire, and post in released state should fail. - EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); - EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); - EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext)); + // Acquire and post in released state should fail. + EXPECT_EQ(-EBUSY, c->Acquire(&fence)); + EXPECT_EQ(-EBUSY, p->Post(LocalHandle())); // Gain in released state should succeed. EXPECT_EQ(0, p->Gain(&fence)); - // Acquire, release, and gain in gained state should fail. - EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); - EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); - EXPECT_EQ(-EALREADY, p->Gain(&fence)); + // Acquire in gained state should fail. + EXPECT_EQ(-EBUSY, c->Acquire(&fence)); } TEST_F(LibBufferHubTest, TestAsyncStateTransitions) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; + EXPECT_EQ(0, p->GainAsync()); - // The producer buffer starts in gained state. - - // Acquire, release, and gain in gained state should fail. + // Acquire in gained state should fail. EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); - EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence)); - EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); // Post in gained state should succeed. EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); EXPECT_EQ(p->buffer_state(), c->buffer_state()); - EXPECT_TRUE(IsBufferPosted(p->buffer_state())); + EXPECT_TRUE(AnyClientPosted(p->buffer_state())); - // Post, release, and gain in posted state should fail. + // Post and gain in posted state should fail. EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence)); - EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence)); EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); @@ -298,7 +279,7 @@ TEST_F(LibBufferHubTest, TestAsyncStateTransitions) { EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); EXPECT_EQ(p->buffer_state(), c->buffer_state()); - EXPECT_TRUE(IsBufferAcquired(p->buffer_state())); + EXPECT_TRUE(AnyClientAcquired(p->buffer_state())); // Acquire, post, and gain in acquired state should fail. EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence)); @@ -313,8 +294,7 @@ TEST_F(LibBufferHubTest, TestAsyncStateTransitions) { EXPECT_EQ(p->buffer_state(), c->buffer_state()); EXPECT_TRUE(IsBufferReleased(p->buffer_state())); - // Release, acquire, and post in released state should fail. - EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence)); + // Acquire and post in released state should fail. EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence)); @@ -323,64 +303,101 @@ TEST_F(LibBufferHubTest, TestAsyncStateTransitions) { EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); EXPECT_EQ(p->buffer_state(), c->buffer_state()); - EXPECT_TRUE(IsBufferGained(p->buffer_state())); + EXPECT_TRUE(AnyClientGained(p->buffer_state())); - // Acquire, release, and gain in gained state should fail. + // Acquire and gain in gained state should fail. EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence)); EXPECT_FALSE(invalid_fence.IsValid()); - EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence)); - EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence)); - EXPECT_FALSE(invalid_fence.IsValid()); } -TEST_F(LibBufferHubTest, TestZeroConsumer) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( +TEST_F(LibBufferHubTest, TestGainTwiceByTheSameProducer) { + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - DvrNativeBufferMetadata metadata; - LocalHandle invalid_fence; + ASSERT_EQ(0, p->GainAsync()); + ASSERT_EQ(0, p->GainAsync()); +} - // Newly created. - EXPECT_TRUE(IsBufferGained(p->buffer_state())); - EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); - EXPECT_TRUE(IsBufferPosted(p->buffer_state())); +TEST_F(LibBufferHubTest, TestGainPostedBuffer) { + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); + ASSERT_TRUE(c.get() != nullptr); + ASSERT_EQ(0, p->GainAsync()); + ASSERT_EQ(0, p->Post(LocalHandle())); + ASSERT_TRUE(AnyClientPosted(p->buffer_state())); - // The buffer should stay in posted stay until a consumer picks it up. - EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); + // Gain in posted state should only succeed with gain_posted_buffer = true. + LocalHandle invalid_fence; + EXPECT_EQ(-EBUSY, p->Gain(&invalid_fence, false)); + EXPECT_EQ(0, p->Gain(&invalid_fence, true)); +} - // A new consumer should still be able to acquire the buffer immediately. - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); +TEST_F(LibBufferHubTest, TestGainPostedBufferAsync) { + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); - EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); - EXPECT_TRUE(IsBufferAcquired(c->buffer_state())); + ASSERT_EQ(0, p->GainAsync()); + ASSERT_EQ(0, p->Post(LocalHandle())); + ASSERT_TRUE(AnyClientPosted(p->buffer_state())); + + // GainAsync in posted state should only succeed with gain_posted_buffer + // equals true. + DvrNativeBufferMetadata metadata; + LocalHandle invalid_fence; + EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence, false)); + EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence, true)); +} + +TEST_F(LibBufferHubTest, TestGainPostedBuffer_noConsumer) { + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); + ASSERT_TRUE(p.get() != nullptr); + ASSERT_EQ(0, p->GainAsync()); + ASSERT_EQ(0, p->Post(LocalHandle())); + // Producer state bit is in released state after post. The overall state of + // the buffer is also released because there is no consumer of this buffer. + ASSERT_TRUE(IsBufferReleased(p->buffer_state())); + + // Gain in released state should succeed. + LocalHandle invalid_fence; + EXPECT_EQ(0, p->Gain(&invalid_fence, false)); } TEST_F(LibBufferHubTest, TestMaxConsumers) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); + uint32_t producer_state_mask = p->client_state_mask(); - std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs; - for (size_t i = 0; i < kMaxConsumerCount; i++) { - cs[i] = BufferConsumer::Import(p->CreateConsumer()); + std::array<std::unique_ptr<ConsumerBuffer>, kMaxConsumerCount> cs; + for (size_t i = 0; i < kMaxConsumerCount; ++i) { + cs[i] = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(cs[i].get() != nullptr); - EXPECT_TRUE(IsBufferGained(cs[i]->buffer_state())); + EXPECT_TRUE(IsBufferReleased(cs[i]->buffer_state())); + EXPECT_NE(producer_state_mask, cs[i]->client_state_mask()); } + EXPECT_EQ(0, p->GainAsync()); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; // Post the producer should trigger all consumers to be available. EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); - EXPECT_TRUE(IsBufferPosted(p->buffer_state())); - for (size_t i = 0; i < kMaxConsumerCount; i++) { + EXPECT_TRUE(IsClientReleased(p->buffer_state(), p->client_state_mask())); + for (size_t i = 0; i < kMaxConsumerCount; ++i) { EXPECT_TRUE( - IsBufferPosted(cs[i]->buffer_state(), cs[i]->buffer_state_bit())); + IsClientPosted(cs[i]->buffer_state(), cs[i]->client_state_mask())); EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(kPollTimeoutMs))); EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence)); - EXPECT_TRUE(IsBufferAcquired(p->buffer_state())); + EXPECT_TRUE( + IsClientAcquired(p->buffer_state(), cs[i]->client_state_mask())); } // All consumers have to release before the buffer is considered to be @@ -400,58 +417,72 @@ TEST_F(LibBufferHubTest, TestMaxConsumers) { } TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferGained) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - EXPECT_TRUE(IsBufferGained(p->buffer_state())); + EXPECT_EQ(0, p->GainAsync()); + EXPECT_TRUE(AnyClientGained(p->buffer_state())); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); - EXPECT_TRUE(IsBufferGained(c->buffer_state())); + EXPECT_TRUE(AnyClientGained(c->buffer_state())); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; // Post the gained buffer should signal already created consumer. EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); - EXPECT_TRUE(IsBufferPosted(p->buffer_state())); + EXPECT_TRUE(AnyClientPosted(p->buffer_state())); EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); - EXPECT_TRUE(IsBufferAcquired(c->buffer_state())); + EXPECT_TRUE(AnyClientAcquired(c->buffer_state())); } -TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( +TEST_F(LibBufferHubTest, TestCreateTheFirstConsumerAfterPostingBuffer) { + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - EXPECT_TRUE(IsBufferGained(p->buffer_state())); + EXPECT_EQ(0, p->GainAsync()); + EXPECT_TRUE(AnyClientGained(p->buffer_state())); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; // Post the gained buffer before any consumer gets created. + // The buffer should be in released state because it is not expected to be + // read by any clients. EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); - EXPECT_TRUE(IsBufferPosted(p->buffer_state())); + EXPECT_TRUE(IsBufferReleased(p->buffer_state())); + EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); - // Newly created consumer should be automatically sigalled. - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + // Newly created consumer will not be signalled for the posted buffer before + // its creation. It cannot acquire the buffer immediately. + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); - EXPECT_TRUE(IsBufferPosted(c->buffer_state())); + EXPECT_FALSE(IsClientPosted(c->buffer_state(), c->client_state_mask())); + EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence)); + + // Producer should be able to gain back and post the buffer + EXPECT_EQ(0, p->GainAsync()); + EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); + + // Consumer should be able to pick up the buffer this time. EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); - EXPECT_TRUE(IsBufferAcquired(c->buffer_state())); + EXPECT_TRUE(IsClientAcquired(c->buffer_state(), c->client_state_mask())); } TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c1 = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c1 = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c1.get() != nullptr); + EXPECT_EQ(0, p->GainAsync()); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; @@ -470,13 +501,13 @@ TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) { // Create another consumer immediately after the release, should not make the // buffer un-released. - std::unique_ptr<BufferConsumer> c2 = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c2 = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); EXPECT_TRUE(IsBufferReleased(p->buffer_state())); EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence)); - EXPECT_TRUE(IsBufferGained(p->buffer_state())); + EXPECT_TRUE(AnyClientGained(p->buffer_state())); } TEST_F(LibBufferHubTest, TestWithCustomMetadata) { @@ -484,23 +515,21 @@ TEST_F(LibBufferHubTest, TestWithCustomMetadata) { int64_t field1; int64_t field2; }; - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); - + EXPECT_EQ(0, p->GainAsync()); Metadata m = {1, 3}; - EXPECT_EQ(0, p->Post(LocalHandle(), m)); + EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(Metadata))); EXPECT_LE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); - LocalHandle fence; Metadata m2 = {}; - EXPECT_EQ(0, c->Acquire(&fence, &m2)); + EXPECT_EQ(0, c->Acquire(&fence, &m2, sizeof(m2))); EXPECT_EQ(m.field1, m2.field1); EXPECT_EQ(m.field2, m2.field2); - EXPECT_EQ(0, c->Release(LocalHandle())); EXPECT_LT(0, RETRY_EINTR(p->Poll(0))); } @@ -515,23 +544,23 @@ TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) { int64_t field2; int64_t field3; }; - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); + EXPECT_EQ(0, p->GainAsync()); // It is illegal to post metadata larger than originally requested during // buffer allocation. OverSizedMetadata evil_meta = {}; - EXPECT_NE(0, p->Post(LocalHandle(), evil_meta)); + EXPECT_NE(0, p->Post(LocalHandle(), &evil_meta, sizeof(OverSizedMetadata))); EXPECT_GE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); // It is ok to post metadata smaller than originally requested during // buffer allocation. - int64_t sequence = 42; - EXPECT_EQ(0, p->Post(LocalHandle(), sequence)); + EXPECT_EQ(0, p->Post(LocalHandle())); } TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { @@ -544,15 +573,16 @@ TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { int64_t field2; int64_t field3; }; - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); + EXPECT_EQ(0, p->GainAsync()); Metadata m = {1, 3}; - EXPECT_EQ(0, p->Post(LocalHandle(), m)); + EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(m))); LocalHandle fence; int64_t sequence; @@ -560,53 +590,56 @@ TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { // It is illegal to acquire metadata larger than originally requested during // buffer allocation. - EXPECT_NE(0, c->Acquire(&fence, &e)); + EXPECT_NE(0, c->Acquire(&fence, &e, sizeof(e))); // It is ok to acquire metadata smaller than originally requested during // buffer allocation. - EXPECT_EQ(0, c->Acquire(&fence, &sequence)); + EXPECT_EQ(0, c->Acquire(&fence, &sequence, sizeof(sequence))); EXPECT_EQ(m.field1, sequence); } TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); + EXPECT_EQ(0, p->GainAsync()); int64_t sequence = 3; - EXPECT_EQ(0, p->Post(LocalHandle(), sequence)); + EXPECT_EQ(0, p->Post(LocalHandle(), &sequence, sizeof(sequence))); LocalHandle fence; EXPECT_EQ(0, c->Acquire(&fence)); } TEST_F(LibBufferHubTest, TestWithNoMeta) { - std::unique_ptr<BufferProducer> p = - BufferProducer::Create(kWidth, kHeight, kFormat, kUsage); + std::unique_ptr<ProducerBuffer> p = + ProducerBuffer::Create(kWidth, kHeight, kFormat, kUsage); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); + EXPECT_EQ(0, p->GainAsync()); LocalHandle fence; - EXPECT_EQ(0, p->Post<void>(LocalHandle())); + EXPECT_EQ(0, p->Post(LocalHandle())); EXPECT_EQ(0, c->Acquire(&fence)); } TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) { - std::unique_ptr<BufferProducer> p = - BufferProducer::Create(kWidth, kHeight, kFormat, kUsage); + std::unique_ptr<ProducerBuffer> p = + ProducerBuffer::Create(kWidth, kHeight, kFormat, kUsage); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); + EXPECT_EQ(0, p->GainAsync()); int64_t sequence = 3; - EXPECT_NE(0, p->Post(LocalHandle(), sequence)); + EXPECT_NE(0, p->Post(LocalHandle(), &sequence, sizeof(sequence))); } namespace { @@ -619,12 +652,13 @@ int PollFd(int fd, int timeout_ms) { } // namespace TEST_F(LibBufferHubTest, TestAcquireFence) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, /*metadata_size=*/0); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); + EXPECT_EQ(0, p->GainAsync()); DvrNativeBufferMetadata meta; LocalHandle f1(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); @@ -680,59 +714,112 @@ TEST_F(LibBufferHubTest, TestAcquireFence) { } TEST_F(LibBufferHubTest, TestOrphanedAcquire) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c1 = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c1 = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c1.get() != nullptr); - const uint64_t consumer_state_bit1 = c1->buffer_state_bit(); + const uint32_t client_state_mask1 = c1->client_state_mask(); + EXPECT_EQ(0, p->GainAsync()); DvrNativeBufferMetadata meta; EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle())); LocalHandle fence; EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs))); - EXPECT_LE(0, c1->AcquireAsync(&meta, &fence)); - // Destroy the consumer now will make it orphaned and the buffer is still - // acquired. + EXPECT_EQ(0, c1->AcquireAsync(&meta, &fence)); + + // Destroy the consumer who has acquired but not released the buffer. c1 = nullptr; - EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); - std::unique_ptr<BufferConsumer> c2 = - BufferConsumer::Import(p->CreateConsumer()); + // The buffer is now available for the producer to gain. + EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); + + // Newly added consumer is not able to acquire the buffer. + std::unique_ptr<ConsumerBuffer> c2 = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); - const uint64_t consumer_state_bit2 = c2->buffer_state_bit(); - EXPECT_NE(consumer_state_bit1, consumer_state_bit2); + const uint32_t client_state_mask2 = c2->client_state_mask(); + EXPECT_NE(client_state_mask1, client_state_mask2); + EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); + EXPECT_EQ(-EBUSY, c2->AcquireAsync(&meta, &fence)); + + // Producer should be able to gain. + EXPECT_EQ(0, p->GainAsync(&meta, &fence, false)); +} + +TEST_F(LibBufferHubTest, TestAcquireLastPosted) { + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); + ASSERT_TRUE(p.get() != nullptr); + std::unique_ptr<ConsumerBuffer> c1 = + ConsumerBuffer::Import(p->CreateConsumer()); + ASSERT_TRUE(c1.get() != nullptr); + const uint32_t client_state_mask1 = c1->client_state_mask(); + + EXPECT_EQ(0, p->GainAsync()); + DvrNativeBufferMetadata meta; + EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle())); + EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs))); - // The new consumer is available for acquire. + // c2 is created when the buffer is in posted state. buffer state for c1 is + // posted. Thus, c2 should be automatically set to posted and able to acquire. + std::unique_ptr<ConsumerBuffer> c2 = + ConsumerBuffer::Import(p->CreateConsumer()); + ASSERT_TRUE(c2.get() != nullptr); + const uint32_t client_state_mask2 = c2->client_state_mask(); + EXPECT_NE(client_state_mask1, client_state_mask2); EXPECT_LT(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - EXPECT_LE(0, c2->AcquireAsync(&meta, &fence)); - // Releasing the consumer makes the buffer gainable. - EXPECT_EQ(0, c2->ReleaseAsync(&meta, LocalHandle())); + LocalHandle invalid_fence; + EXPECT_EQ(0, c2->AcquireAsync(&meta, &invalid_fence)); + + EXPECT_EQ(0, c1->AcquireAsync(&meta, &invalid_fence)); + + // c3 is created when the buffer is in acquired state. buffer state for c1 and + // c2 are acquired. Thus, c3 should be automatically set to posted and able to + // acquire. + std::unique_ptr<ConsumerBuffer> c3 = + ConsumerBuffer::Import(p->CreateConsumer()); + ASSERT_TRUE(c3.get() != nullptr); + const uint32_t client_state_mask3 = c3->client_state_mask(); + EXPECT_NE(client_state_mask1, client_state_mask3); + EXPECT_NE(client_state_mask2, client_state_mask3); + EXPECT_LT(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs))); + EXPECT_EQ(0, c3->AcquireAsync(&meta, &invalid_fence)); + + // Releasing c2 and c3 in normal ways. + EXPECT_EQ(0, c2->Release(LocalHandle())); + EXPECT_EQ(0, c3->ReleaseAsync(&meta, LocalHandle())); + + // Destroy the c1 who has not released the buffer. + c1 = nullptr; // The buffer is now available for the producer to gain. EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); - // But if another consumer is created in released state. - std::unique_ptr<BufferConsumer> c3 = - BufferConsumer::Import(p->CreateConsumer()); - ASSERT_TRUE(c3.get() != nullptr); - const uint64_t consumer_state_bit3 = c3->buffer_state_bit(); - EXPECT_NE(consumer_state_bit2, consumer_state_bit3); - // The consumer buffer is not acquirable. + // C4 is created in released state. Thus, it cannot gain the just posted + // buffer. + std::unique_ptr<ConsumerBuffer> c4 = + ConsumerBuffer::Import(p->CreateConsumer()); + ASSERT_TRUE(c4.get() != nullptr); + const uint32_t client_state_mask4 = c4->client_state_mask(); + EXPECT_NE(client_state_mask3, client_state_mask4); EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs))); - EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &fence)); + EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &invalid_fence)); - // Producer should be able to gain no matter what. - EXPECT_EQ(0, p->GainAsync(&meta, &fence)); + // Producer should be able to gain. + EXPECT_EQ(0, p->GainAsync(&meta, &invalid_fence)); } TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + // TODO(b/112338294) rewrite test after migration + return; + + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(p.get() != nullptr); ASSERT_TRUE(c.get() != nullptr); @@ -741,6 +828,7 @@ TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) { int p_id = p->id(); // Detach in posted state should fail. + EXPECT_EQ(0, p->GainAsync()); EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0); auto s1 = p->Detach(); @@ -789,159 +877,114 @@ TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) { // is gone. EXPECT_EQ(s3.error(), EPIPE); - // Detached buffer handle can be use to construct a new DetachedBuffer object. - auto d = DetachedBuffer::Import(std::move(handle)); + // Detached buffer handle can be use to construct a new BufferHubBuffer + // object. + auto d = BufferHubBuffer::Import(std::move(handle)); EXPECT_FALSE(handle.valid()); EXPECT_TRUE(d->IsConnected()); EXPECT_TRUE(d->IsValid()); - ASSERT_TRUE(d->buffer() != nullptr); - EXPECT_EQ(d->buffer()->initCheck(), 0); EXPECT_EQ(d->id(), p_id); } -TEST_F(LibBufferHubTest, TestCreateDetachedBufferFails) { +TEST_F(LibBufferHubTest, TestCreateBufferHubBufferFails) { // Buffer Creation will fail: BLOB format requires height to be 1. - auto b1 = DetachedBuffer::Create(kWidth, /*height=2*/ 2, kLayerCount, - /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, - kUserMetadataSize); + auto b1 = BufferHubBuffer::Create(kWidth, /*height=2*/ 2, kLayerCount, + /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, + kUserMetadataSize); EXPECT_FALSE(b1->IsConnected()); EXPECT_FALSE(b1->IsValid()); - EXPECT_TRUE(b1->buffer() == nullptr); // Buffer Creation will fail: user metadata size too large. - auto b2 = DetachedBuffer::Create( + auto b2 = BufferHubBuffer::Create( kWidth, kHeight, kLayerCount, kFormat, kUsage, /*user_metadata_size=*/std::numeric_limits<size_t>::max()); EXPECT_FALSE(b2->IsConnected()); EXPECT_FALSE(b2->IsValid()); - EXPECT_TRUE(b2->buffer() == nullptr); // Buffer Creation will fail: user metadata size too large. - auto b3 = DetachedBuffer::Create( + auto b3 = BufferHubBuffer::Create( kWidth, kHeight, kLayerCount, kFormat, kUsage, /*user_metadata_size=*/std::numeric_limits<size_t>::max() - kMetadataHeaderSize); EXPECT_FALSE(b3->IsConnected()); EXPECT_FALSE(b3->IsValid()); - EXPECT_TRUE(b3->buffer() == nullptr); } -TEST_F(LibBufferHubTest, TestCreateDetachedBuffer) { - auto b1 = DetachedBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, - kUsage, kUserMetadataSize); - int b1_id = b1->id(); - +TEST_F(LibBufferHubTest, TestCreateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, + kUsage, kUserMetadataSize); EXPECT_TRUE(b1->IsConnected()); EXPECT_TRUE(b1->IsValid()); - ASSERT_TRUE(b1->buffer() != nullptr); EXPECT_NE(b1->id(), 0); - EXPECT_EQ(b1->buffer()->initCheck(), 0); - EXPECT_FALSE(b1->buffer()->isDetachedBuffer()); - - // Takes a standalone GraphicBuffer which still holds on an - // PDX::LocalChannelHandle towards BufferHub. - sp<GraphicBuffer> g1 = b1->TakeGraphicBuffer(); - ASSERT_TRUE(g1 != nullptr); - EXPECT_TRUE(g1->isDetachedBuffer()); - - EXPECT_FALSE(b1->IsConnected()); - EXPECT_FALSE(b1->IsValid()); - EXPECT_TRUE(b1->buffer() == nullptr); - - sp<GraphicBuffer> g2 = b1->TakeGraphicBuffer(); - ASSERT_TRUE(g2 == nullptr); - - auto h1 = g1->takeDetachedBufferHandle(); - ASSERT_TRUE(h1 != nullptr); - ASSERT_TRUE(h1->isValid()); - EXPECT_FALSE(g1->isDetachedBuffer()); - - auto b2 = DetachedBuffer::Import(std::move(h1->handle())); - ASSERT_FALSE(h1->isValid()); - EXPECT_TRUE(b2->IsConnected()); - EXPECT_TRUE(b2->IsValid()); - - ASSERT_TRUE(b2->buffer() != nullptr); - EXPECT_EQ(b2->buffer()->initCheck(), 0); - - // The newly created DetachedBuffer should share the original buffer_id. - EXPECT_EQ(b2->id(), b1_id); - EXPECT_FALSE(b2->buffer()->isDetachedBuffer()); } -TEST_F(LibBufferHubTest, TestPromoteDetachedBuffer) { - auto b1 = DetachedBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, - kUsage, kUserMetadataSize); - int b1_id = b1->id(); - EXPECT_TRUE(b1->IsValid()); +TEST_F(LibBufferHubTest, TestDetach) { + // TODO(b/112338294) rewrite test after migration + return; - auto status_or_handle = b1->Promote(); - EXPECT_TRUE(status_or_handle); - - // The detached buffer should have hangup. - EXPECT_GT(RETRY_EINTR(b1->Poll(kPollTimeoutMs)), 0); - auto status_or_int = b1->GetEventMask(POLLHUP); - EXPECT_TRUE(status_or_int.ok()); - EXPECT_EQ(status_or_int.get(), POLLHUP); - - // The buffer client is still considered as connected but invalid. - EXPECT_TRUE(b1->IsConnected()); - EXPECT_FALSE(b1->IsValid()); - - // Gets the channel handle for the producer. - LocalChannelHandle h1 = status_or_handle.take(); - EXPECT_TRUE(h1.valid()); - - std::unique_ptr<BufferProducer> p1 = BufferProducer::Import(std::move(h1)); - EXPECT_FALSE(h1.valid()); - ASSERT_TRUE(p1 != nullptr); - int p1_id = p1->id(); - - // A newly promoted ProducerBuffer should inherit the same buffer id. - EXPECT_EQ(b1_id, p1_id); - EXPECT_TRUE(IsBufferGained(p1->buffer_state())); -} - -TEST_F(LibBufferHubTest, TestDetachThenPromote) { - std::unique_ptr<BufferProducer> p1 = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p1 = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p1.get() != nullptr); int p1_id = p1->id(); - // Detached the producer. + // Detached the producer from gained state. + EXPECT_EQ(0, p1->GainAsync()); auto status_or_handle = p1->Detach(); EXPECT_TRUE(status_or_handle.ok()); LocalChannelHandle h1 = status_or_handle.take(); EXPECT_TRUE(h1.valid()); - // Detached buffer handle can be use to construct a new DetachedBuffer object. - auto b1 = DetachedBuffer::Import(std::move(h1)); + // Detached buffer handle can be use to construct a new BufferHubBuffer + // object. + auto b1 = BufferHubBuffer::Import(std::move(h1)); EXPECT_FALSE(h1.valid()); EXPECT_TRUE(b1->IsValid()); int b1_id = b1->id(); EXPECT_EQ(b1_id, p1_id); +} + +TEST_F(LibBufferHubTest, TestDuplicateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, + kUsage, kUserMetadataSize); + int b1_id = b1->id(); + EXPECT_TRUE(b1->IsValid()); + EXPECT_EQ(b1->user_metadata_size(), kUserMetadataSize); + EXPECT_NE(b1->client_state_mask(), 0U); - // Promote the detached buffer. - status_or_handle = b1->Promote(); - // The buffer client is still considered as connected but invalid. + auto status_or_handle = b1->Duplicate(); + EXPECT_TRUE(status_or_handle); + + // The detached buffer should still be valid. EXPECT_TRUE(b1->IsConnected()); - EXPECT_FALSE(b1->IsValid()); - EXPECT_TRUE(status_or_handle.ok()); + EXPECT_TRUE(b1->IsValid()); - // Gets the channel handle for the producer. + // Gets the channel handle for the duplicated buffer. LocalChannelHandle h2 = status_or_handle.take(); EXPECT_TRUE(h2.valid()); - std::unique_ptr<BufferProducer> p2 = BufferProducer::Import(std::move(h2)); + std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(std::move(h2)); EXPECT_FALSE(h2.valid()); - ASSERT_TRUE(p2 != nullptr); - int p2_id = p2->id(); + ASSERT_TRUE(b2 != nullptr); + EXPECT_TRUE(b2->IsValid()); + EXPECT_EQ(b2->user_metadata_size(), kUserMetadataSize); + EXPECT_NE(b2->client_state_mask(), 0U); + + int b2_id = b2->id(); + + // These two buffer instances are based on the same physical buffer under the + // hood, so they should share the same id. + EXPECT_EQ(b1_id, b2_id); + // We use client_state_mask() to tell those two instances apart. + EXPECT_NE(b1->client_state_mask(), b2->client_state_mask()); + + // Both buffer instances should be in gained state. + EXPECT_TRUE(IsBufferReleased(b1->buffer_state())); + EXPECT_TRUE(IsBufferReleased(b2->buffer_state())); - // A newly promoted ProducerBuffer should inherit the same buffer id. - EXPECT_EQ(b1_id, p2_id); - EXPECT_TRUE(IsBufferGained(p2->buffer_state())); + // TODO(b/112338294) rewrite test after migration + return; } diff --git a/libs/vr/libbufferhub/buffer_hub_base.cpp b/libs/vr/libbufferhub/buffer_hub_base.cpp new file mode 100644 index 0000000000..8497f3edc1 --- /dev/null +++ b/libs/vr/libbufferhub/buffer_hub_base.cpp @@ -0,0 +1,229 @@ +#include <poll.h> +#include <sys/epoll.h> + +#include <pdx/default_transport/client_channel.h> +#include <pdx/default_transport/client_channel_factory.h> +#include <private/dvr/buffer_hub_base.h> + +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Status; +using android::pdx::default_transport::ClientChannel; +using android::pdx::default_transport::ClientChannelFactory; + +namespace android { +namespace dvr { + +BufferHubBase::BufferHubBase(LocalChannelHandle channel_handle) + : Client{pdx::default_transport::ClientChannel::Create( + std::move(channel_handle))}, + id_(-1), + cid_(-1) {} +BufferHubBase::BufferHubBase(const std::string& endpoint_path) + : Client{pdx::default_transport::ClientChannelFactory::Create( + endpoint_path)}, + id_(-1), + cid_(-1) {} + +BufferHubBase::~BufferHubBase() { + // buffer_state and fence_state are not reset here. They will be used to + // clean up epoll fd if necessary in ProducerChannel::RemoveConsumer method. + if (metadata_header_ != nullptr) { + metadata_buffer_.Unlock(); + } +} + +Status<LocalChannelHandle> BufferHubBase::CreateConsumer() { + Status<LocalChannelHandle> status = + InvokeRemoteMethod<BufferHubRPC::NewConsumer>(); + ALOGE_IF(!status, + "BufferHub::CreateConsumer: Failed to create consumer channel: %s", + status.GetErrorMessage().c_str()); + return status; +} + +int BufferHubBase::ImportBuffer() { + ATRACE_NAME("BufferHubBase::ImportBuffer"); + + Status<BufferDescription<LocalHandle>> status = + InvokeRemoteMethod<BufferHubRPC::GetBuffer>(); + if (!status) { + ALOGE("BufferHubBase::ImportBuffer: Failed to get buffer: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } else if (status.get().id() < 0) { + ALOGE("BufferHubBase::ImportBuffer: Received an invalid id!"); + return -EIO; + } + + auto buffer_desc = status.take(); + + // Stash the buffer id to replace the value in id_. + const int new_id = buffer_desc.id(); + + // Import the buffer. + IonBuffer ion_buffer; + ALOGD_IF(TRACE, "BufferHubBase::ImportBuffer: id=%d.", buffer_desc.id()); + + if (const int ret = buffer_desc.ImportBuffer(&ion_buffer)) + return ret; + + // Import the metadata. + IonBuffer metadata_buffer; + if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) { + ALOGE("Failed to import metadata buffer, error=%d", ret); + return ret; + } + size_t metadata_buf_size = metadata_buffer.width(); + if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) { + ALOGE("BufferHubBase::ImportBuffer: metadata buffer too small: %zu", + metadata_buf_size); + return -ENOMEM; + } + + // If all imports succee, replace the previous buffer and id. + buffer_ = std::move(ion_buffer); + metadata_buffer_ = std::move(metadata_buffer); + metadata_buf_size_ = metadata_buf_size; + user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize; + + void* metadata_ptr = nullptr; + if (const int ret = + metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0, + /*y=*/0, metadata_buf_size_, + /*height=*/1, &metadata_ptr)) { + ALOGE("BufferHubBase::ImportBuffer: Failed to lock metadata."); + return ret; + } + + // Set up shared fences. + shared_acquire_fence_ = buffer_desc.take_acquire_fence(); + shared_release_fence_ = buffer_desc.take_release_fence(); + if (!shared_acquire_fence_ || !shared_release_fence_) { + ALOGE("BufferHubBase::ImportBuffer: Failed to import shared fences."); + return -EIO; + } + + metadata_header_ = + reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr); + if (user_metadata_size_) { + user_metadata_ptr_ = + reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_ptr) + + BufferHubDefs::kMetadataHeaderSize); + } else { + user_metadata_ptr_ = nullptr; + } + + id_ = new_id; + cid_ = buffer_desc.buffer_cid(); + client_state_mask_ = buffer_desc.client_state_mask(); + + // Note that here the buffer_state, fence_state and active_clients_bit_mask + // are mapped from shared memory as an atomic object. The std::atomic's + // constructor will not be called so that the original value stored in the + // memory region will be preserved. + buffer_state_ = &metadata_header_->buffer_state; + ALOGD_IF(TRACE, + "BufferHubBase::ImportBuffer: id=%d, buffer_state=%" PRIx32 ".", + id(), buffer_state_->load(std::memory_order_acquire)); + fence_state_ = &metadata_header_->fence_state; + ALOGD_IF(TRACE, + "BufferHubBase::ImportBuffer: id=%d, fence_state=%" PRIx32 ".", id(), + fence_state_->load(std::memory_order_acquire)); + active_clients_bit_mask_ = &metadata_header_->active_clients_bit_mask; + ALOGD_IF( + TRACE, + "BufferHubBase::ImportBuffer: id=%d, active_clients_bit_mask=%" PRIx32 + ".", + id(), active_clients_bit_mask_->load(std::memory_order_acquire)); + + return 0; +} + +int BufferHubBase::CheckMetadata(size_t user_metadata_size) const { + if (user_metadata_size && !user_metadata_ptr_) { + ALOGE("BufferHubBase::CheckMetadata: doesn't support custom metadata."); + return -EINVAL; + } + if (user_metadata_size > user_metadata_size_) { + ALOGE("BufferHubBase::CheckMetadata: too big: %zu, maximum: %zu.", + user_metadata_size, user_metadata_size_); + return -E2BIG; + } + return 0; +} + +int BufferHubBase::UpdateSharedFence(const LocalHandle& new_fence, + const LocalHandle& shared_fence) { + if (pending_fence_fd_.Get() != new_fence.Get()) { + // First, replace the old fd if there was already one. Skipping if the new + // one is the same as the old. + if (pending_fence_fd_.IsValid()) { + const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL, + pending_fence_fd_.Get(), nullptr); + ALOGW_IF(ret, + "BufferHubBase::UpdateSharedFence: failed to remove old fence " + "fd from epoll set, error: %s.", + strerror(errno)); + } + + if (new_fence.IsValid()) { + // If ready fence is valid, we put that into the epoll set. + epoll_event event; + event.events = EPOLLIN; + event.data.u32 = client_state_mask(); + pending_fence_fd_ = new_fence.Duplicate(); + if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(), + &event) < 0) { + const int error = errno; + ALOGE( + "BufferHubBase::UpdateSharedFence: failed to add new fence fd " + "into epoll set, error: %s.", + strerror(error)); + return -error; + } + // Set bit in fence state to indicate that there is a fence from this + // producer or consumer. + fence_state_->fetch_or(client_state_mask()); + } else { + // Unset bit in fence state to indicate that there is no fence, so that + // when consumer to acquire or producer to acquire, it knows no need to + // check fence for this buffer. + fence_state_->fetch_and(~client_state_mask()); + } + } + + return 0; +} + +int BufferHubBase::Poll(int timeout_ms) { + ATRACE_NAME("BufferHubBase::Poll"); + pollfd p = {event_fd(), POLLIN, 0}; + return poll(&p, 1, timeout_ms); +} + +int BufferHubBase::Lock(int usage, int x, int y, int width, int height, + void** address) { + return buffer_.Lock(usage, x, y, width, height, address); +} + +int BufferHubBase::Unlock() { return buffer_.Unlock(); } + +int BufferHubBase::GetBlobReadWritePointer(size_t size, void** addr) { + int width = static_cast<int>(size); + int height = 1; + int ret = Lock(usage(), 0, 0, width, height, addr); + if (ret == 0) + Unlock(); + return ret; +} + +void BufferHubBase::GetBlobFds(int* fds, size_t* fds_count, + size_t max_fds_count) const { + size_t numFds = static_cast<size_t>(native_handle()->numFds); + *fds_count = std::min(max_fds_count, numFds); + std::copy(native_handle()->data, native_handle()->data + *fds_count, fds); +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp deleted file mode 100644 index 159f2bd1b3..0000000000 --- a/libs/vr/libbufferhub/buffer_hub_client.cpp +++ /dev/null @@ -1,650 +0,0 @@ -#include <private/dvr/buffer_hub_client.h> - -#include <log/log.h> -#include <poll.h> -#include <sys/epoll.h> -#include <utils/Trace.h> - -#include <mutex> - -#include <pdx/default_transport/client_channel.h> -#include <pdx/default_transport/client_channel_factory.h> - -#include "include/private/dvr/bufferhub_rpc.h" - -using android::pdx::LocalChannelHandle; -using android::pdx::LocalHandle; -using android::pdx::Status; -using android::pdx::default_transport::ClientChannel; -using android::pdx::default_transport::ClientChannelFactory; - -namespace android { -namespace dvr { - -BufferHubClient::BufferHubClient() - : Client(ClientChannelFactory::Create(BufferHubRPC::kClientPath)) {} - -BufferHubClient::BufferHubClient(LocalChannelHandle channel_handle) - : Client(ClientChannel::Create(std::move(channel_handle))) {} - -bool BufferHubClient::IsValid() const { - return IsConnected() && GetChannelHandle().valid(); -} - -LocalChannelHandle BufferHubClient::TakeChannelHandle() { - if (IsConnected()) { - return std::move(GetChannelHandle()); - } else { - return {}; - } -} - -BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle) - : Client{pdx::default_transport::ClientChannel::Create( - std::move(channel_handle))}, - id_(-1) {} -BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path) - : Client{pdx::default_transport::ClientChannelFactory::Create( - endpoint_path)}, - id_(-1) {} - -BufferHubBuffer::~BufferHubBuffer() { - if (metadata_header_ != nullptr) { - metadata_buffer_.Unlock(); - } -} - -Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() { - Status<LocalChannelHandle> status = - InvokeRemoteMethod<BufferHubRPC::NewConsumer>(); - ALOGE_IF(!status, - "BufferHub::CreateConsumer: Failed to create consumer channel: %s", - status.GetErrorMessage().c_str()); - return status; -} - -int BufferHubBuffer::ImportBuffer() { - ATRACE_NAME("BufferHubBuffer::ImportBuffer"); - - Status<BufferDescription<LocalHandle>> status = - InvokeRemoteMethod<BufferHubRPC::GetBuffer>(); - if (!status) { - ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } else if (status.get().id() < 0) { - ALOGE("BufferHubBuffer::ImportBuffer: Received an invalid id!"); - return -EIO; - } - - auto buffer_desc = status.take(); - - // Stash the buffer id to replace the value in id_. - const int new_id = buffer_desc.id(); - - // Import the buffer. - IonBuffer ion_buffer; - ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d.", buffer_desc.id()); - - if (const int ret = buffer_desc.ImportBuffer(&ion_buffer)) - return ret; - - // Import the metadata. - IonBuffer metadata_buffer; - if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) { - ALOGE("Failed to import metadata buffer, error=%d", ret); - return ret; - } - size_t metadata_buf_size = metadata_buffer.width(); - if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) { - ALOGE("BufferHubBuffer::ImportBuffer: metadata buffer too small: %zu", - metadata_buf_size); - return -ENOMEM; - } - - // If all imports succee, replace the previous buffer and id. - buffer_ = std::move(ion_buffer); - metadata_buffer_ = std::move(metadata_buffer); - metadata_buf_size_ = metadata_buf_size; - user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize; - - void* metadata_ptr = nullptr; - if (const int ret = - metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0, - /*y=*/0, metadata_buf_size_, - /*height=*/1, &metadata_ptr)) { - ALOGE("BufferHubBuffer::ImportBuffer: Failed to lock metadata."); - return ret; - } - - // Set up shared fences. - shared_acquire_fence_ = buffer_desc.take_acquire_fence(); - shared_release_fence_ = buffer_desc.take_release_fence(); - if (!shared_acquire_fence_ || !shared_release_fence_) { - ALOGE("BufferHubBuffer::ImportBuffer: Failed to import shared fences."); - return -EIO; - } - - metadata_header_ = - reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr); - if (user_metadata_size_) { - user_metadata_ptr_ = - reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_ptr) + - BufferHubDefs::kMetadataHeaderSize); - } else { - user_metadata_ptr_ = nullptr; - } - - id_ = new_id; - buffer_state_bit_ = buffer_desc.buffer_state_bit(); - - // Note that here the buffer state is mapped from shared memory as an atomic - // object. The std::atomic's constructor will not be called so that the - // original value stored in the memory region will be preserved. - buffer_state_ = &metadata_header_->buffer_state; - ALOGD_IF(TRACE, - "BufferHubBuffer::ImportBuffer: id=%d, buffer_state=%" PRIx64 ".", - id(), buffer_state_->load()); - fence_state_ = &metadata_header_->fence_state; - ALOGD_IF(TRACE, - "BufferHubBuffer::ImportBuffer: id=%d, fence_state=%" PRIx64 ".", - id(), fence_state_->load()); - - return 0; -} - -inline int BufferHubBuffer::CheckMetadata(size_t user_metadata_size) const { - if (user_metadata_size && !user_metadata_ptr_) { - ALOGE("BufferHubBuffer::CheckMetadata: doesn't support custom metadata."); - return -EINVAL; - } - if (user_metadata_size > user_metadata_size_) { - ALOGE("BufferHubBuffer::CheckMetadata: too big: %zu, maximum: %zu.", - user_metadata_size, user_metadata_size_); - return -E2BIG; - } - return 0; -} - -int BufferHubBuffer::UpdateSharedFence(const LocalHandle& new_fence, - const LocalHandle& shared_fence) { - if (pending_fence_fd_.Get() != new_fence.Get()) { - // First, replace the old fd if there was already one. Skipping if the new - // one is the same as the old. - if (pending_fence_fd_.IsValid()) { - const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL, - pending_fence_fd_.Get(), nullptr); - ALOGW_IF(ret, - "BufferHubBuffer::UpdateSharedFence: failed to remove old fence " - "fd from epoll set, error: %s.", - strerror(errno)); - } - - if (new_fence.IsValid()) { - // If ready fence is valid, we put that into the epoll set. - epoll_event event; - event.events = EPOLLIN; - event.data.u64 = buffer_state_bit(); - pending_fence_fd_ = new_fence.Duplicate(); - if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(), - &event) < 0) { - const int error = errno; - ALOGE( - "BufferHubBuffer::UpdateSharedFence: failed to add new fence fd " - "into epoll set, error: %s.", - strerror(error)); - return -error; - } - // Set bit in fence state to indicate that there is a fence from this - // producer or consumer. - fence_state_->fetch_or(buffer_state_bit()); - } else { - // Unset bit in fence state to indicate that there is no fence, so that - // when consumer to acquire or producer to acquire, it knows no need to - // check fence for this buffer. - fence_state_->fetch_and(~buffer_state_bit()); - } - } - - return 0; -} - -int BufferHubBuffer::Poll(int timeout_ms) { - ATRACE_NAME("BufferHubBuffer::Poll"); - pollfd p = {event_fd(), POLLIN, 0}; - return poll(&p, 1, timeout_ms); -} - -int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height, - void** address) { - return buffer_.Lock(usage, x, y, width, height, address); -} - -int BufferHubBuffer::Unlock() { return buffer_.Unlock(); } - -int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) { - int width = static_cast<int>(size); - int height = 1; - int ret = Lock(usage(), 0, 0, width, height, addr); - if (ret == 0) - Unlock(); - return ret; -} - -int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) { - return GetBlobReadWritePointer(size, addr); -} - -void BufferHubBuffer::GetBlobFds(int* fds, size_t* fds_count, - size_t max_fds_count) const { - size_t numFds = static_cast<size_t>(native_handle()->numFds); - *fds_count = std::min(max_fds_count, numFds); - std::copy(native_handle()->data, native_handle()->data + *fds_count, fds); -} - -BufferConsumer::BufferConsumer(LocalChannelHandle channel) - : BASE(std::move(channel)) { - const int ret = ImportBuffer(); - if (ret < 0) { - ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s", - strerror(-ret)); - Close(ret); - } -} - -std::unique_ptr<BufferConsumer> BufferConsumer::Import( - LocalChannelHandle channel) { - ATRACE_NAME("BufferConsumer::Import"); - ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value()); - return BufferConsumer::Create(std::move(channel)); -} - -std::unique_ptr<BufferConsumer> BufferConsumer::Import( - Status<LocalChannelHandle> status) { - return Import(status ? status.take() - : LocalChannelHandle{nullptr, -status.error()}); -} - -int BufferConsumer::LocalAcquire(DvrNativeBufferMetadata* out_meta, - LocalHandle* out_fence) { - if (!out_meta) - return -EINVAL; - - // Only check producer bit and this consumer buffer's particular consumer bit. - // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit - // is not set. - uint64_t buffer_state = buffer_state_->load(); - if (!BufferHubDefs::IsBufferPosted(buffer_state, buffer_state_bit())) { - ALOGE("BufferConsumer::LocalAcquire: not posted, id=%d state=%" PRIx64 - " buffer_state_bit=%" PRIx64 ".", - id(), buffer_state, buffer_state_bit()); - return -EBUSY; - } - - // Copy the canonical metadata. - void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata); - memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata)); - // Fill in the user_metadata_ptr in address space of the local process. - if (out_meta->user_metadata_size) { - out_meta->user_metadata_ptr = - reinterpret_cast<uint64_t>(user_metadata_ptr_); - } else { - out_meta->user_metadata_ptr = 0; - } - - uint64_t fence_state = fence_state_->load(); - // If there is an acquire fence from producer, we need to return it. - if (fence_state & BufferHubDefs::kProducerStateBit) { - *out_fence = shared_acquire_fence_.Duplicate(); - } - - // Set the consumer bit unique to this consumer. - BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, buffer_state_bit()); - return 0; -} - -int BufferConsumer::Acquire(LocalHandle* ready_fence) { - return Acquire(ready_fence, nullptr, 0); -} - -int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta, - size_t user_metadata_size) { - ATRACE_NAME("BufferConsumer::Acquire"); - - if (const int error = CheckMetadata(user_metadata_size)) - return error; - - DvrNativeBufferMetadata canonical_meta; - if (const int error = LocalAcquire(&canonical_meta, ready_fence)) - return error; - - if (meta && user_metadata_size) { - void* metadata_src = - reinterpret_cast<void*>(canonical_meta.user_metadata_ptr); - if (metadata_src) { - memcpy(meta, metadata_src, user_metadata_size); - } else { - ALOGW("BufferConsumer::Acquire: no user-defined metadata."); - } - } - - auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerAcquire>(); - if (!status) - return -status.error(); - return 0; -} - -int BufferConsumer::AcquireAsync(DvrNativeBufferMetadata* out_meta, - LocalHandle* out_fence) { - ATRACE_NAME("BufferConsumer::AcquireAsync"); - - if (const int error = LocalAcquire(out_meta, out_fence)) - return error; - - auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode); - if (!status) - return -status.error(); - return 0; -} - -int BufferConsumer::LocalRelease(const DvrNativeBufferMetadata* meta, - const LocalHandle& release_fence) { - if (const int error = CheckMetadata(meta->user_metadata_size)) - return error; - - // Check invalid state transition. - uint64_t buffer_state = buffer_state_->load(); - if (!BufferHubDefs::IsBufferAcquired(buffer_state)) { - ALOGE("BufferConsumer::LocalRelease: not acquired id=%d state=%" PRIx64 ".", - id(), buffer_state); - return -EBUSY; - } - - // On release, only the user requested metadata is copied back into the shared - // memory for metadata. Since there are multiple consumers, it doesn't make - // sense to send the canonical metadata back to the producer. However, one of - // the consumer can still choose to write up to user_metadata_size bytes of - // data into user_metadata_ptr. - if (meta->user_metadata_ptr && meta->user_metadata_size) { - void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr); - memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); - } - - // Send out the release fence through the shared epoll fd. Note that during - // releasing the producer is not expected to be polling on the fence. - if (const int error = UpdateSharedFence(release_fence, shared_release_fence_)) - return error; - - // For release operation, the client don't need to change the state as it's - // bufferhubd's job to flip the produer bit once all consumers are released. - return 0; -} - -int BufferConsumer::Release(const LocalHandle& release_fence) { - ATRACE_NAME("BufferConsumer::Release"); - - DvrNativeBufferMetadata meta; - if (const int error = LocalRelease(&meta, release_fence)) - return error; - - return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>( - BorrowedFence(release_fence.Borrow()))); -} - -int BufferConsumer::ReleaseAsync() { - DvrNativeBufferMetadata meta; - return ReleaseAsync(&meta, LocalHandle()); -} - -int BufferConsumer::ReleaseAsync(const DvrNativeBufferMetadata* meta, - const LocalHandle& release_fence) { - ATRACE_NAME("BufferConsumer::ReleaseAsync"); - - if (const int error = LocalRelease(meta, release_fence)) - return error; - - return ReturnStatusOrError( - SendImpulse(BufferHubRPC::ConsumerRelease::Opcode)); -} - -int BufferConsumer::Discard() { return Release(LocalHandle()); } - -int BufferConsumer::SetIgnore(bool ignore) { - return ReturnStatusOrError( - InvokeRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(ignore)); -} - -BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format, - uint64_t usage, size_t user_metadata_size) - : BASE(BufferHubRPC::kClientPath) { - ATRACE_NAME("BufferProducer::BufferProducer"); - ALOGD_IF(TRACE, - "BufferProducer::BufferProducer: fd=%d width=%u height=%u format=%u " - "usage=%" PRIx64 " user_metadata_size=%zu", - event_fd(), width, height, format, usage, user_metadata_size); - - auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( - width, height, format, usage, user_metadata_size); - if (!status) { - ALOGE( - "BufferProducer::BufferProducer: Failed to create producer buffer: %s", - status.GetErrorMessage().c_str()); - Close(-status.error()); - return; - } - - const int ret = ImportBuffer(); - if (ret < 0) { - ALOGE( - "BufferProducer::BufferProducer: Failed to import producer buffer: %s", - strerror(-ret)); - Close(ret); - } -} - -BufferProducer::BufferProducer(uint64_t usage, size_t size) - : BASE(BufferHubRPC::kClientPath) { - ATRACE_NAME("BufferProducer::BufferProducer"); - ALOGD_IF(TRACE, "BufferProducer::BufferProducer: usage=%" PRIx64 " size=%zu", - usage, size); - const int width = static_cast<int>(size); - const int height = 1; - const int format = HAL_PIXEL_FORMAT_BLOB; - const size_t user_metadata_size = 0; - - auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( - width, height, format, usage, user_metadata_size); - if (!status) { - ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s", - status.GetErrorMessage().c_str()); - Close(-status.error()); - return; - } - - const int ret = ImportBuffer(); - if (ret < 0) { - ALOGE( - "BufferProducer::BufferProducer: Failed to import producer buffer: %s", - strerror(-ret)); - Close(ret); - } -} - -BufferProducer::BufferProducer(LocalChannelHandle channel) - : BASE(std::move(channel)) { - const int ret = ImportBuffer(); - if (ret < 0) { - ALOGE( - "BufferProducer::BufferProducer: Failed to import producer buffer: %s", - strerror(-ret)); - Close(ret); - } -} - -int BufferProducer::LocalPost(const DvrNativeBufferMetadata* meta, - const LocalHandle& ready_fence) { - if (const int error = CheckMetadata(meta->user_metadata_size)) - return error; - - // Check invalid state transition. - uint64_t buffer_state = buffer_state_->load(); - if (!BufferHubDefs::IsBufferGained(buffer_state)) { - ALOGE("BufferProducer::LocalPost: not gained, id=%d state=%" PRIx64 ".", - id(), buffer_state); - return -EBUSY; - } - - // Copy the canonical metadata. - void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata); - memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata)); - // Copy extra user requested metadata. - if (meta->user_metadata_ptr && meta->user_metadata_size) { - void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr); - memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); - } - - // Send out the acquire fence through the shared epoll fd. Note that during - // posting no consumer is not expected to be polling on the fence. - if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_)) - return error; - - // Set the producer bit atomically to transit into posted state. - BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, - BufferHubDefs::kProducerStateBit); - return 0; -} - -int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta, - size_t user_metadata_size) { - ATRACE_NAME("BufferProducer::Post"); - - // Populate cononical metadata for posting. - DvrNativeBufferMetadata canonical_meta; - canonical_meta.user_metadata_ptr = reinterpret_cast<uint64_t>(meta); - canonical_meta.user_metadata_size = user_metadata_size; - - if (const int error = LocalPost(&canonical_meta, ready_fence)) - return error; - - return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>( - BorrowedFence(ready_fence.Borrow()))); -} - -int BufferProducer::PostAsync(const DvrNativeBufferMetadata* meta, - const LocalHandle& ready_fence) { - ATRACE_NAME("BufferProducer::PostAsync"); - - if (const int error = LocalPost(meta, ready_fence)) - return error; - - return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode)); -} - -int BufferProducer::LocalGain(DvrNativeBufferMetadata* out_meta, - LocalHandle* out_fence) { - uint64_t buffer_state = buffer_state_->load(); - ALOGD_IF(TRACE, "BufferProducer::LocalGain: buffer=%d, state=%" PRIx64 ".", - id(), buffer_state); - - if (!out_meta) - return -EINVAL; - - if (!BufferHubDefs::IsBufferReleased(buffer_state)) { - if (BufferHubDefs::IsBufferGained(buffer_state)) { - // We don't want to log error when gaining a newly allocated - // buffer. - ALOGI("BufferProducer::LocalGain: already gained id=%d.", id()); - return -EALREADY; - } - ALOGE("BufferProducer::LocalGain: not released id=%d state=%" PRIx64 ".", - id(), buffer_state); - return -EBUSY; - } - - // Canonical metadata is undefined on Gain. Except for user_metadata and - // release_fence_mask. Fill in the user_metadata_ptr in address space of the - // local process. - if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) { - out_meta->user_metadata_size = - metadata_header_->metadata.user_metadata_size; - out_meta->user_metadata_ptr = - reinterpret_cast<uint64_t>(user_metadata_ptr_); - } else { - out_meta->user_metadata_size = 0; - out_meta->user_metadata_ptr = 0; - } - - uint64_t fence_state = fence_state_->load(); - // If there is an release fence from consumer, we need to return it. - if (fence_state & BufferHubDefs::kConsumerStateMask) { - *out_fence = shared_release_fence_.Duplicate(); - out_meta->release_fence_mask = - fence_state & BufferHubDefs::kConsumerStateMask; - } - - // Clear out all bits and the buffer is now back to gained state. - buffer_state_->store(0ULL); - return 0; -} - -int BufferProducer::Gain(LocalHandle* release_fence) { - ATRACE_NAME("BufferProducer::Gain"); - - DvrNativeBufferMetadata meta; - if (const int error = LocalGain(&meta, release_fence)) - return error; - - auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>(); - if (!status) - return -status.error(); - return 0; -} - -int BufferProducer::GainAsync(DvrNativeBufferMetadata* out_meta, - LocalHandle* release_fence) { - ATRACE_NAME("BufferProducer::GainAsync"); - - if (const int error = LocalGain(out_meta, release_fence)) - return error; - - return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode)); -} - -int BufferProducer::GainAsync() { - DvrNativeBufferMetadata meta; - LocalHandle fence; - return GainAsync(&meta, &fence); -} - -std::unique_ptr<BufferProducer> BufferProducer::Import( - LocalChannelHandle channel) { - ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value()); - return BufferProducer::Create(std::move(channel)); -} - -std::unique_ptr<BufferProducer> BufferProducer::Import( - Status<LocalChannelHandle> status) { - return Import(status ? status.take() - : LocalChannelHandle{nullptr, -status.error()}); -} - -Status<LocalChannelHandle> BufferProducer::Detach() { - uint64_t buffer_state = buffer_state_->load(); - if (!BufferHubDefs::IsBufferGained(buffer_state)) { - // Can only detach a BufferProducer when it's in gained state. - ALOGW("BufferProducer::Detach: The buffer (id=%d, state=0x%" PRIx64 - ") is not in gained state.", - id(), buffer_state); - return {}; - } - - Status<LocalChannelHandle> status = - InvokeRemoteMethod<BufferHubRPC::ProducerBufferDetach>(); - ALOGE_IF(!status, - "BufferProducer::Detach: Failed to detach buffer (id=%d): %s.", id(), - status.GetErrorMessage().c_str()); - return status; -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libbufferhub/consumer_buffer.cpp b/libs/vr/libbufferhub/consumer_buffer.cpp new file mode 100644 index 0000000000..b6ca64eef2 --- /dev/null +++ b/libs/vr/libbufferhub/consumer_buffer.cpp @@ -0,0 +1,213 @@ +#include <private/dvr/consumer_buffer.h> + +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Status; + +namespace android { +namespace dvr { + +ConsumerBuffer::ConsumerBuffer(LocalChannelHandle channel) + : BASE(std::move(channel)) { + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE("ConsumerBuffer::ConsumerBuffer: Failed to import buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +std::unique_ptr<ConsumerBuffer> ConsumerBuffer::Import( + LocalChannelHandle channel) { + ATRACE_NAME("ConsumerBuffer::Import"); + ALOGD_IF(TRACE, "ConsumerBuffer::Import: channel=%d", channel.value()); + return ConsumerBuffer::Create(std::move(channel)); +} + +std::unique_ptr<ConsumerBuffer> ConsumerBuffer::Import( + Status<LocalChannelHandle> status) { + return Import(status ? status.take() + : LocalChannelHandle{nullptr, -status.error()}); +} + +int ConsumerBuffer::LocalAcquire(DvrNativeBufferMetadata* out_meta, + LocalHandle* out_fence) { + if (!out_meta) + return -EINVAL; + + // The buffer can be acquired iff the buffer state for this client is posted. + uint32_t current_buffer_state = + buffer_state_->load(std::memory_order_acquire); + if (!BufferHubDefs::IsClientPosted(current_buffer_state, + client_state_mask())) { + ALOGE( + "%s: Failed to acquire the buffer. The buffer is not posted, id=%d " + "state=%" PRIx32 " client_state_mask=%" PRIx32 ".", + __FUNCTION__, id(), current_buffer_state, client_state_mask()); + return -EBUSY; + } + + // Change the buffer state for this consumer from posted to acquired. + uint32_t updated_buffer_state = current_buffer_state ^ client_state_mask(); + while (!buffer_state_->compare_exchange_weak( + current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, + std::memory_order_acquire)) { + ALOGD( + "%s Failed to acquire the buffer. Current buffer state was changed to " + "%" PRIx32 + " when trying to acquire the buffer and modify the buffer state to " + "%" PRIx32 ". About to try again if the buffer is still posted.", + __FUNCTION__, current_buffer_state, updated_buffer_state); + if (!BufferHubDefs::IsClientPosted(current_buffer_state, + client_state_mask())) { + ALOGE( + "%s: Failed to acquire the buffer. The buffer is no longer posted, " + "id=%d state=%" PRIx32 " client_state_mask=%" PRIx32 ".", + __FUNCTION__, id(), current_buffer_state, client_state_mask()); + return -EBUSY; + } + // The failure of compare_exchange_weak updates current_buffer_state. + updated_buffer_state = current_buffer_state ^ client_state_mask(); + } + + // Copy the canonical metadata. + void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata); + memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata)); + // Fill in the user_metadata_ptr in address space of the local process. + if (out_meta->user_metadata_size) { + out_meta->user_metadata_ptr = + reinterpret_cast<uint64_t>(user_metadata_ptr_); + } else { + out_meta->user_metadata_ptr = 0; + } + + uint32_t fence_state = fence_state_->load(std::memory_order_acquire); + // If there is an acquire fence from producer, we need to return it. + // The producer state bit mask is kFirstClientBitMask for now. + if (fence_state & BufferHubDefs::kFirstClientBitMask) { + *out_fence = shared_acquire_fence_.Duplicate(); + } + + return 0; +} + +int ConsumerBuffer::Acquire(LocalHandle* ready_fence) { + return Acquire(ready_fence, nullptr, 0); +} + +int ConsumerBuffer::Acquire(LocalHandle* ready_fence, void* meta, + size_t user_metadata_size) { + ATRACE_NAME("ConsumerBuffer::Acquire"); + + if (const int error = CheckMetadata(user_metadata_size)) + return error; + + DvrNativeBufferMetadata canonical_meta; + if (const int error = LocalAcquire(&canonical_meta, ready_fence)) + return error; + + if (meta && user_metadata_size) { + void* metadata_src = + reinterpret_cast<void*>(canonical_meta.user_metadata_ptr); + if (metadata_src) { + memcpy(meta, metadata_src, user_metadata_size); + } else { + ALOGW("ConsumerBuffer::Acquire: no user-defined metadata."); + } + } + + auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerAcquire>(); + if (!status) + return -status.error(); + return 0; +} + +int ConsumerBuffer::AcquireAsync(DvrNativeBufferMetadata* out_meta, + LocalHandle* out_fence) { + ATRACE_NAME("ConsumerBuffer::AcquireAsync"); + + if (const int error = LocalAcquire(out_meta, out_fence)) + return error; + + auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode); + if (!status) + return -status.error(); + return 0; +} + +int ConsumerBuffer::LocalRelease(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence) { + if (const int error = CheckMetadata(meta->user_metadata_size)) + return error; + + // Set the buffer state of this client to released if it is not already in + // released state. + uint32_t current_buffer_state = + buffer_state_->load(std::memory_order_acquire); + if (BufferHubDefs::IsClientReleased(current_buffer_state, + client_state_mask())) { + return 0; + } + uint32_t updated_buffer_state = current_buffer_state & (~client_state_mask()); + while (!buffer_state_->compare_exchange_weak( + current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, + std::memory_order_acquire)) { + ALOGD( + "%s: Failed to release the buffer. Current buffer state was changed to " + "%" PRIx32 + " when trying to release the buffer and modify the buffer state to " + "%" PRIx32 ". About to try again.", + __FUNCTION__, current_buffer_state, updated_buffer_state); + // The failure of compare_exchange_weak updates current_buffer_state. + updated_buffer_state = current_buffer_state & (~client_state_mask()); + } + + // On release, only the user requested metadata is copied back into the shared + // memory for metadata. Since there are multiple consumers, it doesn't make + // sense to send the canonical metadata back to the producer. However, one of + // the consumer can still choose to write up to user_metadata_size bytes of + // data into user_metadata_ptr. + if (meta->user_metadata_ptr && meta->user_metadata_size) { + void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr); + memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); + } + + // Send out the release fence through the shared epoll fd. Note that during + // releasing the producer is not expected to be polling on the fence. + if (const int error = UpdateSharedFence(release_fence, shared_release_fence_)) + return error; + + return 0; +} + +int ConsumerBuffer::Release(const LocalHandle& release_fence) { + ATRACE_NAME("ConsumerBuffer::Release"); + + DvrNativeBufferMetadata meta; + if (const int error = LocalRelease(&meta, release_fence)) + return error; + + return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>( + BorrowedFence(release_fence.Borrow()))); +} + +int ConsumerBuffer::ReleaseAsync() { + DvrNativeBufferMetadata meta; + return ReleaseAsync(&meta, LocalHandle()); +} + +int ConsumerBuffer::ReleaseAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence) { + ATRACE_NAME("ConsumerBuffer::ReleaseAsync"); + + if (const int error = LocalRelease(meta, release_fence)) + return error; + + return ReturnStatusOrError( + SendImpulse(BufferHubRPC::ConsumerRelease::Opcode)); +} + +int ConsumerBuffer::Discard() { return Release(LocalHandle()); } + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhub/detached_buffer.cpp b/libs/vr/libbufferhub/detached_buffer.cpp deleted file mode 100644 index 6fae16d265..0000000000 --- a/libs/vr/libbufferhub/detached_buffer.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include <private/dvr/detached_buffer.h> - -#include <pdx/file_handle.h> -#include <ui/DetachedBufferHandle.h> - -#include <poll.h> - -using android::pdx::LocalChannelHandle; -using android::pdx::LocalHandle; -using android::pdx::Status; - -namespace android { -namespace dvr { - -DetachedBuffer::DetachedBuffer(uint32_t width, uint32_t height, - uint32_t layer_count, uint32_t format, - uint64_t usage, size_t user_metadata_size) { - ATRACE_NAME("DetachedBuffer::DetachedBuffer"); - ALOGD_IF(TRACE, - "DetachedBuffer::DetachedBuffer: width=%u height=%u layer_count=%u, " - "format=%u usage=%" PRIx64 " user_metadata_size=%zu", - width, height, layer_count, format, usage, user_metadata_size); - - auto status = client_.InvokeRemoteMethod<DetachedBufferRPC::Create>( - width, height, layer_count, format, usage, user_metadata_size); - if (!status) { - ALOGE( - "DetachedBuffer::DetachedBuffer: Failed to create detached buffer: %s", - status.GetErrorMessage().c_str()); - client_.Close(-status.error()); - } - - const int ret = ImportGraphicBuffer(); - if (ret < 0) { - ALOGE("DetachedBuffer::DetachedBuffer: Failed to import buffer: %s", - strerror(-ret)); - client_.Close(ret); - } -} - -DetachedBuffer::DetachedBuffer(LocalChannelHandle channel_handle) - : client_(std::move(channel_handle)) { - const int ret = ImportGraphicBuffer(); - if (ret < 0) { - ALOGE("DetachedBuffer::DetachedBuffer: Failed to import buffer: %s", - strerror(-ret)); - client_.Close(ret); - } -} - -int DetachedBuffer::ImportGraphicBuffer() { - ATRACE_NAME("DetachedBuffer::DetachedBuffer"); - - auto status = client_.InvokeRemoteMethod<DetachedBufferRPC::Import>(); - if (!status) { - ALOGE("DetachedBuffer::DetachedBuffer: Failed to import GraphicBuffer: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - - BufferDescription<LocalHandle> buffer_desc = status.take(); - if (buffer_desc.id() < 0) { - ALOGE("DetachedBuffer::DetachedBuffer: Received an invalid id!"); - return -EIO; - } - - // Stash the buffer id to replace the value in id_. - const int buffer_id = buffer_desc.id(); - - // Import the buffer. - IonBuffer ion_buffer; - ALOGD_IF(TRACE, "DetachedBuffer::DetachedBuffer: id=%d.", buffer_id); - - if (const int ret = buffer_desc.ImportBuffer(&ion_buffer)) { - ALOGE("Failed to import GraphicBuffer, error=%d", ret); - return ret; - } - - // If all imports succeed, replace the previous buffer and id. - id_ = buffer_id; - buffer_ = std::move(ion_buffer); - return 0; -} - -int DetachedBuffer::Poll(int timeout_ms) { - ATRACE_NAME("DetachedBuffer::Poll"); - pollfd p = {client_.event_fd(), POLLIN, 0}; - return poll(&p, 1, timeout_ms); -} - -Status<LocalChannelHandle> DetachedBuffer::Promote() { - ATRACE_NAME("DetachedBuffer::Promote"); - ALOGD_IF(TRACE, "DetachedBuffer::Promote: id=%d.", id_); - - auto status_or_handle = - client_.InvokeRemoteMethod<DetachedBufferRPC::Promote>(); - if (status_or_handle.ok()) { - // Invalidate the buffer. - buffer_ = {}; - } else { - ALOGE("DetachedBuffer::Promote: Failed to promote buffer (id=%d): %s.", id_, - status_or_handle.GetErrorMessage().c_str()); - } - return status_or_handle; -} - -sp<GraphicBuffer> DetachedBuffer::TakeGraphicBuffer() { - if (!client_.IsValid() || !buffer_.buffer()) { - ALOGE("DetachedBuffer::TakeGraphicBuffer: Invalid buffer."); - return nullptr; - } - - // Technically this should never happen. - LOG_FATAL_IF( - buffer_.buffer()->isDetachedBuffer(), - "DetachedBuffer::TakeGraphicBuffer: GraphicBuffer is already detached."); - - sp<GraphicBuffer> buffer = std::move(buffer_.buffer()); - buffer->setDetachedBufferHandle( - DetachedBufferHandle::Create(client_.TakeChannelHandle())); - return buffer; -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h new file mode 100644 index 0000000000..440a59dc71 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h @@ -0,0 +1,170 @@ +#ifndef ANDROID_DVR_BUFFER_HUB_BASE_H_ +#define ANDROID_DVR_BUFFER_HUB_BASE_H_ + +#include <vector> + +#include <private/dvr/bufferhub_rpc.h> + +namespace android { +namespace dvr { + +// Base class of two types of BufferHub clients: dvr::ProducerBuffer and +// dvr::ConsumerBuffer. +class BufferHubBase : public pdx::Client { + public: + using LocalHandle = pdx::LocalHandle; + using LocalChannelHandle = pdx::LocalChannelHandle; + template <typename T> + using Status = pdx::Status<T>; + + // Create a new consumer channel that is attached to the producer. Returns + // a file descriptor for the new channel or a negative error code. + Status<LocalChannelHandle> CreateConsumer(); + + // Polls the fd for |timeout_ms| milliseconds (-1 for infinity). + int Poll(int timeout_ms); + + // Locks the area specified by (x, y, width, height) for a specific usage. If + // the usage is software then |addr| will be updated to point to the address + // of the buffer in virtual memory. The caller should only access/modify the + // pixels in the specified area. anything else is undefined behavior. + int Lock(int usage, int x, int y, int width, int height, void** addr); + + // Must be called after Lock() when the caller has finished changing the + // buffer. + int Unlock(); + + // Gets a blob buffer that was created with ProducerBuffer::CreateBlob. + // Locking and Unlocking is handled internally. There's no need to Unlock + // after calling this method. + int GetBlobReadWritePointer(size_t size, void** addr); + + // Returns a dup'd file descriptor for accessing the blob shared memory. The + // caller takes ownership of the file descriptor and must close it or pass on + // ownership. Some GPU API extensions can take file descriptors to bind shared + // memory gralloc buffers to GPU buffer objects. + LocalHandle GetBlobFd() const { + // Current GPU vendor puts the buffer allocation in one FD. If we change GPU + // vendors and this is the wrong fd, late-latching and EDS will very clearly + // stop working and we will need to correct this. The alternative is to use + // a GL context in the pose service to allocate this buffer or to use the + // ION API directly instead of gralloc. + return LocalHandle(dup(native_handle()->data[0])); + } + + // Get up to |max_fds_count| file descriptors for accessing the blob shared + // memory. |fds_count| will contain the actual number of file descriptors. + void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const; + + using Client::event_fd; + + Status<int> GetEventMask(int events) { + if (auto* client_channel = GetChannel()) { + return client_channel->GetEventMask(events); + } else { + return pdx::ErrorStatus(EINVAL); + } + } + + std::vector<pdx::ClientChannel::EventSource> GetEventSources() const { + if (auto* client_channel = GetChannel()) { + return client_channel->GetEventSources(); + } else { + return {}; + } + } + + native_handle_t* native_handle() const { + return const_cast<native_handle_t*>(buffer_.handle()); + } + + IonBuffer* buffer() { return &buffer_; } + const IonBuffer* buffer() const { return &buffer_; } + + // Gets ID of the buffer client. All BufferHub clients derived from the same + // buffer in bufferhubd share the same buffer id. + int id() const { return id_; } + + // Gets the channel id of the buffer client. Each BufferHub client has its + // system unique channel id. + int cid() const { return cid_; } + + // Returns the buffer buffer state. + uint32_t buffer_state() { + return buffer_state_->load(std::memory_order_acquire); + }; + + // A state mask which is unique to a buffer hub client among all its siblings + // sharing the same concrete graphic buffer. + uint32_t client_state_mask() const { return client_state_mask_; } + + // The following methods return settings of the first buffer. Currently, + // it is only possible to create multi-buffer BufferHubBases with the same + // settings. + uint32_t width() const { return buffer_.width(); } + uint32_t height() const { return buffer_.height(); } + uint32_t stride() const { return buffer_.stride(); } + uint32_t format() const { return buffer_.format(); } + uint32_t usage() const { return buffer_.usage(); } + uint32_t layer_count() const { return buffer_.layer_count(); } + + uint64_t GetQueueIndex() const { return metadata_header_->queue_index; } + void SetQueueIndex(uint64_t index) { metadata_header_->queue_index = index; } + + protected: + explicit BufferHubBase(LocalChannelHandle channel); + explicit BufferHubBase(const std::string& endpoint_path); + virtual ~BufferHubBase(); + + // Initialization helper. + int ImportBuffer(); + + // Check invalid metadata operation. Returns 0 if requested metadata is valid. + int CheckMetadata(size_t user_metadata_size) const; + + // Send out the new fence by updating the shared fence (shared_release_fence + // for producer and shared_acquire_fence for consumer). Note that during this + // should only be used in LocalPost() or LocalRelease, and the shared fence + // shouldn't be poll'ed by the other end. + int UpdateSharedFence(const LocalHandle& new_fence, + const LocalHandle& shared_fence); + + // IonBuffer that is shared between bufferhubd, producer, and consumers. + size_t metadata_buf_size_{0}; + size_t user_metadata_size_{0}; + BufferHubDefs::MetadataHeader* metadata_header_ = nullptr; + void* user_metadata_ptr_ = nullptr; + std::atomic<uint32_t>* buffer_state_ = nullptr; + std::atomic<uint32_t>* fence_state_ = nullptr; + std::atomic<uint32_t>* active_clients_bit_mask_ = nullptr; + + LocalHandle shared_acquire_fence_; + LocalHandle shared_release_fence_; + + // A local fence fd that holds the ownership of the fence fd on Post (for + // producer) and Release (for consumer). + LocalHandle pending_fence_fd_; + + private: + BufferHubBase(const BufferHubBase&) = delete; + void operator=(const BufferHubBase&) = delete; + + // Global id for the buffer that is consistent across processes. It is meant + // for logging and debugging purposes only and should not be used for lookup + // or any other functional purpose as a security precaution. + int id_; + + // Channel id. + int cid_; + + // Client bit mask which indicates the locations of this client object in the + // buffer_state_. + uint32_t client_state_mask_{0U}; + IonBuffer buffer_; + IonBuffer metadata_buffer_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFER_HUB_BASE_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h deleted file mode 100644 index 0ea77c85fb..0000000000 --- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h +++ /dev/null @@ -1,347 +0,0 @@ -#ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_ -#define ANDROID_DVR_BUFFER_HUB_CLIENT_H_ - -#include <hardware/gralloc.h> -#include <pdx/channel_handle.h> -#include <pdx/client.h> -#include <pdx/file_handle.h> -#include <pdx/status.h> - -#include <vector> - -#include <private/dvr/ion_buffer.h> - -#include "bufferhub_rpc.h" - -namespace android { -namespace dvr { - -class BufferHubClient : public pdx::Client { - public: - BufferHubClient(); - explicit BufferHubClient(pdx::LocalChannelHandle channel_handle); - - bool IsValid() const; - pdx::LocalChannelHandle TakeChannelHandle(); - - using pdx::Client::Close; - using pdx::Client::GetChannel; - using pdx::Client::InvokeRemoteMethod; - using pdx::Client::IsConnected; - using pdx::Client::event_fd; -}; - -class BufferHubBuffer : public pdx::Client { - public: - using LocalHandle = pdx::LocalHandle; - using LocalChannelHandle = pdx::LocalChannelHandle; - template <typename T> - using Status = pdx::Status<T>; - - // Create a new consumer channel that is attached to the producer. Returns - // a file descriptor for the new channel or a negative error code. - Status<LocalChannelHandle> CreateConsumer(); - - // Polls the fd for |timeout_ms| milliseconds (-1 for infinity). - int Poll(int timeout_ms); - - // Locks the area specified by (x, y, width, height) for a specific usage. If - // the usage is software then |addr| will be updated to point to the address - // of the buffer in virtual memory. The caller should only access/modify the - // pixels in the specified area. anything else is undefined behavior. - int Lock(int usage, int x, int y, int width, int height, void** addr); - - // Must be called after Lock() when the caller has finished changing the - // buffer. - int Unlock(); - - // Gets a blob buffer that was created with BufferProducer::CreateBlob. - // Locking and Unlocking is handled internally. There's no need to Unlock - // after calling this method. - int GetBlobReadWritePointer(size_t size, void** addr); - - // Gets a blob buffer that was created with BufferProducer::CreateBlob. - // Locking and Unlocking is handled internally. There's no need to Unlock - // after calling this method. - int GetBlobReadOnlyPointer(size_t size, void** addr); - - // Returns a dup'd file descriptor for accessing the blob shared memory. The - // caller takes ownership of the file descriptor and must close it or pass on - // ownership. Some GPU API extensions can take file descriptors to bind shared - // memory gralloc buffers to GPU buffer objects. - LocalHandle GetBlobFd() const { - // Current GPU vendor puts the buffer allocation in one FD. If we change GPU - // vendors and this is the wrong fd, late-latching and EDS will very clearly - // stop working and we will need to correct this. The alternative is to use - // a GL context in the pose service to allocate this buffer or to use the - // ION API directly instead of gralloc. - return LocalHandle(dup(native_handle()->data[0])); - } - - // Get up to |max_fds_count| file descriptors for accessing the blob shared - // memory. |fds_count| will contain the actual number of file descriptors. - void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const; - - using Client::event_fd; - - Status<int> GetEventMask(int events) { - if (auto* client_channel = GetChannel()) { - return client_channel->GetEventMask(events); - } else { - return pdx::ErrorStatus(EINVAL); - } - } - - std::vector<pdx::ClientChannel::EventSource> GetEventSources() const { - if (auto* client_channel = GetChannel()) { - return client_channel->GetEventSources(); - } else { - return {}; - } - } - - native_handle_t* native_handle() const { - return const_cast<native_handle_t*>(buffer_.handle()); - } - - IonBuffer* buffer() { return &buffer_; } - const IonBuffer* buffer() const { return &buffer_; } - - int id() const { return id_; } - - // Returns the buffer buffer state. - uint64_t buffer_state() { return buffer_state_->load(); }; - - // A state mask which is unique to a buffer hub client among all its siblings - // sharing the same concrete graphic buffer. - uint64_t buffer_state_bit() const { return buffer_state_bit_; } - - // The following methods return settings of the first buffer. Currently, - // it is only possible to create multi-buffer BufferHubBuffers with the same - // settings. - uint32_t width() const { return buffer_.width(); } - uint32_t height() const { return buffer_.height(); } - uint32_t stride() const { return buffer_.stride(); } - uint32_t format() const { return buffer_.format(); } - uint32_t usage() const { return buffer_.usage(); } - uint32_t layer_count() const { return buffer_.layer_count(); } - - uint64_t GetQueueIndex() const { return metadata_header_->queue_index; } - void SetQueueIndex(uint64_t index) { metadata_header_->queue_index = index; } - - protected: - explicit BufferHubBuffer(LocalChannelHandle channel); - explicit BufferHubBuffer(const std::string& endpoint_path); - virtual ~BufferHubBuffer(); - - // Initialization helper. - int ImportBuffer(); - - // Check invalid metadata operation. Returns 0 if requested metadata is valid. - int CheckMetadata(size_t user_metadata_size) const; - - // Send out the new fence by updating the shared fence (shared_release_fence - // for producer and shared_acquire_fence for consumer). Note that during this - // should only be used in LocalPost() or LocalRelease, and the shared fence - // shouldn't be poll'ed by the other end. - int UpdateSharedFence(const LocalHandle& new_fence, - const LocalHandle& shared_fence); - - // IonBuffer that is shared between bufferhubd, producer, and consumers. - size_t metadata_buf_size_{0}; - size_t user_metadata_size_{0}; - BufferHubDefs::MetadataHeader* metadata_header_{nullptr}; - void* user_metadata_ptr_{nullptr}; - std::atomic<uint64_t>* buffer_state_{nullptr}; - std::atomic<uint64_t>* fence_state_{nullptr}; - - LocalHandle shared_acquire_fence_; - LocalHandle shared_release_fence_; - - // A local fence fd that holds the ownership of the fence fd on Post (for - // producer) and Release (for consumer). - LocalHandle pending_fence_fd_; - - private: - BufferHubBuffer(const BufferHubBuffer&) = delete; - void operator=(const BufferHubBuffer&) = delete; - - // Global id for the buffer that is consistent across processes. It is meant - // for logging and debugging purposes only and should not be used for lookup - // or any other functional purpose as a security precaution. - int id_; - uint64_t buffer_state_bit_{0ULL}; - IonBuffer buffer_; - IonBuffer metadata_buffer_; -}; - -// This represents a writable buffer. Calling Post notifies all clients and -// makes the buffer read-only. Call Gain to acquire write access. A buffer -// may have many consumers. -// -// The user of BufferProducer is responsible with making sure that the Post() is -// done with the correct metadata type and size. The user is also responsible -// for making sure that remote ends (BufferConsumers) are also using the correct -// metadata when acquiring the buffer. The API guarantees that a Post() with a -// metadata of wrong size will fail. However, it currently does not do any -// type checking. -// The API also assumes that metadata is a serializable type (plain old data). -class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> { - public: - // Imports a bufferhub producer channel, assuming ownership of its handle. - static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel); - static std::unique_ptr<BufferProducer> Import( - Status<LocalChannelHandle> status); - - // Asynchronously posts a buffer. The fence and metadata are passed to - // consumer via shared fd and shared memory. - int PostAsync(const DvrNativeBufferMetadata* meta, - const LocalHandle& ready_fence); - - // Post this buffer, passing |ready_fence| to the consumers. The bytes in - // |meta| are passed unaltered to the consumers. The producer must not modify - // the buffer until it is re-gained. - // This returns zero or a negative unix error code. - int Post(const LocalHandle& ready_fence, const void* meta, - size_t user_metadata_size); - - template <typename Meta, - typename = typename std::enable_if<std::is_void<Meta>::value>::type> - int Post(const LocalHandle& ready_fence) { - return Post(ready_fence, nullptr, 0); - } - template <typename Meta, typename = typename std::enable_if< - !std::is_void<Meta>::value>::type> - int Post(const LocalHandle& ready_fence, const Meta& meta) { - return Post(ready_fence, &meta, sizeof(meta)); - } - - // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it - // must be waited on before using the buffer. If it is not valid then the - // buffer is free for immediate use. This call will only succeed if the buffer - // is in the released state. - // This returns zero or a negative unix error code. - int Gain(LocalHandle* release_fence); - int GainAsync(); - - // Asynchronously marks a released buffer as gained. This method is similar to - // the synchronous version above, except that it does not wait for BufferHub - // to acknowledge success or failure. Because of the asynchronous nature of - // the underlying message, no error is returned if this method is called when - // the buffer is in an incorrect state. Returns zero if sending the message - // succeeded, or a negative errno code if local error check fails. - int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); - - // Detaches a ProducerBuffer from an existing producer/consumer set. Can only - // be called when a producer buffer has exclusive access to the buffer (i.e. - // in the gain'ed state). On the successful return of the IPC call, a new - // LocalChannelHandle representing a detached buffer will be returned and all - // existing producer and consumer channels will be closed. Further IPCs - // towards those channels will return error. - Status<LocalChannelHandle> Detach(); - - private: - friend BASE; - - // Constructors are automatically exposed through BufferProducer::Create(...) - // static template methods inherited from ClientBase, which take the same - // arguments as the constructors. - - // Constructs a buffer with the given geometry and parameters. - BufferProducer(uint32_t width, uint32_t height, uint32_t format, - uint64_t usage, size_t metadata_size = 0); - - // Constructs a blob (flat) buffer with the given usage flags. - BufferProducer(uint64_t usage, size_t size); - - // Imports the given file handle to a producer channel, taking ownership. - explicit BufferProducer(LocalChannelHandle channel); - - // Local state transition helpers. - int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); - int LocalPost(const DvrNativeBufferMetadata* meta, - const LocalHandle& ready_fence); -}; - -// This is a connection to a producer buffer, which can be located in another -// application. When that buffer is Post()ed, this fd will be signaled and -// Acquire allows read access. The user is responsible for making sure that -// Acquire is called with the correct metadata structure. The only guarantee the -// API currently provides is that an Acquire() with metadata of the wrong size -// will fail. -class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> { - public: - // This call assumes ownership of |fd|. - static std::unique_ptr<BufferConsumer> Import(LocalChannelHandle channel); - static std::unique_ptr<BufferConsumer> Import( - Status<LocalChannelHandle> status); - - // Attempt to retrieve a post event from buffer hub. If successful, - // |ready_fence| will be set to a fence to wait on until the buffer is ready. - // This call will only succeed after the fd is signalled. This call may be - // performed as an alternative to the Acquire() with metadata. In such cases - // the metadata is not read. - // - // This returns zero or negative unix error code. - int Acquire(LocalHandle* ready_fence); - - // Attempt to retrieve a post event from buffer hub. If successful, - // |ready_fence| is set to a fence signaling that the contents of the buffer - // are available. This call will only succeed if the buffer is in the posted - // state. - // Returns zero on success, or a negative errno code otherwise. - int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size); - - // Attempt to retrieve a post event from buffer hub. If successful, - // |ready_fence| is set to a fence to wait on until the buffer is ready. This - // call will only succeed after the fd is signaled. This returns zero or a - // negative unix error code. - template <typename Meta> - int Acquire(LocalHandle* ready_fence, Meta* meta) { - return Acquire(ready_fence, meta, sizeof(*meta)); - } - - // Asynchronously acquires a bufer. - int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); - - // This should be called after a successful Acquire call. If the fence is - // valid the fence determines the buffer usage, otherwise the buffer is - // released immediately. - // This returns zero or a negative unix error code. - int Release(const LocalHandle& release_fence); - int ReleaseAsync(); - - // Asynchronously releases a buffer. Similar to the synchronous version above, - // except that it does not wait for BufferHub to reply with success or error. - // The fence and metadata are passed to consumer via shared fd and shared - // memory. - int ReleaseAsync(const DvrNativeBufferMetadata* meta, - const LocalHandle& release_fence); - - // May be called after or instead of Acquire to indicate that the consumer - // does not need to access the buffer this cycle. This returns zero or a - // negative unix error code. - int Discard(); - - // When set, this consumer is no longer notified when this buffer is - // available. The system behaves as if Discard() is immediately called - // whenever the buffer is posted. If ignore is set to true while a buffer is - // pending, it will act as if Discard() was also called. - // This returns zero or a negative unix error code. - int SetIgnore(bool ignore); - - private: - friend BASE; - - explicit BufferConsumer(LocalChannelHandle channel); - - // Local state transition helpers. - int LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); - int LocalRelease(const DvrNativeBufferMetadata* meta, - const LocalHandle& release_fence); -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_BUFFER_HUB_CLIENT_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h new file mode 100644 index 0000000000..f2c40fe3d3 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h @@ -0,0 +1,201 @@ +#ifndef ANDROID_DVR_BUFFER_HUB_DEFS_H_ +#define ANDROID_DVR_BUFFER_HUB_DEFS_H_ + +#include <dvr/dvr_api.h> +#include <hardware/gralloc.h> +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> +#include <pdx/rpc/remote_method.h> +#include <pdx/rpc/serializable.h> +#include <private/dvr/native_handle_wrapper.h> +#include <ui/BufferHubDefs.h> + +namespace android { +namespace dvr { + +namespace BufferHubDefs { + +static constexpr uint32_t kMetadataFormat = HAL_PIXEL_FORMAT_BLOB; +static constexpr uint32_t kMetadataUsage = + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN; + +// See more details in libs/ui/include/ui/BufferHubDefs.h +static constexpr int kMaxNumberOfClients = + android::BufferHubDefs::kMaxNumberOfClients; +static constexpr uint32_t kLowbitsMask = android::BufferHubDefs::kLowbitsMask; +static constexpr uint32_t kHighBitsMask = android::BufferHubDefs::kHighBitsMask; +static constexpr uint32_t kFirstClientBitMask = + android::BufferHubDefs::kFirstClientBitMask; + +static inline bool AnyClientGained(uint32_t state) { + return android::BufferHubDefs::AnyClientGained(state); +} + +static inline bool IsClientGained(uint32_t state, uint32_t client_bit_mask) { + return android::BufferHubDefs::IsClientGained(state, client_bit_mask); +} + +static inline bool AnyClientPosted(uint32_t state) { + return android::BufferHubDefs::AnyClientPosted(state); +} + +static inline bool IsClientPosted(uint32_t state, uint32_t client_bit_mask) { + return android::BufferHubDefs::IsClientPosted(state, client_bit_mask); +} + +static inline bool AnyClientAcquired(uint32_t state) { + return android::BufferHubDefs::AnyClientAcquired(state); +} + +static inline bool IsClientAcquired(uint32_t state, uint32_t client_bit_mask) { + return android::BufferHubDefs::IsClientAcquired(state, client_bit_mask); +} + +static inline bool IsBufferReleased(uint32_t state) { + return android::BufferHubDefs::IsBufferReleased(state); +} + +static inline bool IsClientReleased(uint32_t state, uint32_t client_bit_mask) { + return android::BufferHubDefs::IsClientReleased(state, client_bit_mask); +} + +// Returns the next available buffer client's client_state_masks. +// @params union_bits. Union of all existing clients' client_state_masks. +static inline uint32_t FindNextAvailableClientStateMask(uint32_t union_bits) { + return android::BufferHubDefs::FindNextAvailableClientStateMask(union_bits); +} + +using MetadataHeader = android::BufferHubDefs::MetadataHeader; +static constexpr size_t kMetadataHeaderSize = + android::BufferHubDefs::kMetadataHeaderSize; + +} // namespace BufferHubDefs + +template <typename FileHandleType> +class BufferTraits { + public: + BufferTraits() = default; + BufferTraits(const native_handle_t* buffer_handle, + const FileHandleType& metadata_handle, int id, + uint32_t client_state_mask, uint64_t metadata_size, + uint32_t width, uint32_t height, uint32_t layer_count, + uint32_t format, uint64_t usage, uint32_t stride, + const FileHandleType& acquire_fence_fd, + const FileHandleType& release_fence_fd) + : id_(id), + client_state_mask_(client_state_mask), + metadata_size_(metadata_size), + width_(width), + height_(height), + layer_count_(layer_count), + format_(format), + usage_(usage), + stride_(stride), + buffer_handle_(buffer_handle), + metadata_handle_(metadata_handle.Borrow()), + acquire_fence_fd_(acquire_fence_fd.Borrow()), + release_fence_fd_(release_fence_fd.Borrow()) {} + + BufferTraits(BufferTraits&& other) = default; + BufferTraits& operator=(BufferTraits&& other) = default; + + // ID of the buffer client. All BufferHubBuffer clients derived from the same + // buffer in bufferhubd share the same buffer id. + int id() const { return id_; } + + // State mask of the buffer client. Each BufferHubBuffer client backed by the + // same buffer channel has uniqued state bit among its siblings. For a + // producer buffer the bit must be kFirstClientBitMask; for a consumer the bit + // must be one of the kConsumerStateMask. + uint32_t client_state_mask() const { return client_state_mask_; } + uint64_t metadata_size() const { return metadata_size_; } + + uint32_t width() { return width_; } + uint32_t height() { return height_; } + uint32_t layer_count() { return layer_count_; } + uint32_t format() { return format_; } + uint64_t usage() { return usage_; } + uint32_t stride() { return stride_; } + + const NativeHandleWrapper<FileHandleType>& buffer_handle() const { + return buffer_handle_; + } + + NativeHandleWrapper<FileHandleType> take_buffer_handle() { + return std::move(buffer_handle_); + } + FileHandleType take_metadata_handle() { return std::move(metadata_handle_); } + FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); } + FileHandleType take_release_fence() { return std::move(release_fence_fd_); } + + private: + // BufferHub specific traits. + int id_ = -1; + uint32_t client_state_mask_; + uint64_t metadata_size_; + + // Traits for a GraphicBuffer. + uint32_t width_; + uint32_t height_; + uint32_t layer_count_; + uint32_t format_; + uint64_t usage_; + uint32_t stride_; + + // Native handle for the graphic buffer. + NativeHandleWrapper<FileHandleType> buffer_handle_; + + // File handle of an ashmem that holds buffer metadata. + FileHandleType metadata_handle_; + + // Pamameters for shared fences. + FileHandleType acquire_fence_fd_; + FileHandleType release_fence_fd_; + + PDX_SERIALIZABLE_MEMBERS(BufferTraits<FileHandleType>, id_, + client_state_mask_, metadata_size_, stride_, width_, + height_, layer_count_, format_, usage_, + buffer_handle_, metadata_handle_, acquire_fence_fd_, + release_fence_fd_); + + BufferTraits(const BufferTraits&) = delete; + void operator=(const BufferTraits&) = delete; +}; + +struct DetachedBufferRPC { + private: + enum { + kOpDetachedBufferBase = 1000, + + // Allocates a standalone DetachedBuffer not associated with any producer + // consumer set. + kOpCreate, + + // Imports the given channel handle to a DetachedBuffer, taking ownership. + kOpImport, + + // Creates a DetachedBuffer client from an existing one. The new client will + // share the same underlying gralloc buffer and ashmem region for metadata. + kOpDuplicate, + }; + + // Aliases. + using LocalChannelHandle = pdx::LocalChannelHandle; + using LocalHandle = pdx::LocalHandle; + using Void = pdx::rpc::Void; + + public: + PDX_REMOTE_METHOD(Create, kOpCreate, + void(uint32_t width, uint32_t height, uint32_t layer_count, + uint32_t format, uint64_t usage, + size_t user_metadata_size)); + PDX_REMOTE_METHOD(Import, kOpImport, BufferTraits<LocalHandle>(Void)); + PDX_REMOTE_METHOD(Duplicate, kOpDuplicate, LocalChannelHandle(Void)); + + PDX_REMOTE_API(API, Create, Import, Duplicate); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFER_HUB_DEFS_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h index 04f4fb4c21..f1cd0b4adc 100644 --- a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h +++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h @@ -1,11 +1,11 @@ #ifndef ANDROID_DVR_BUFFERHUB_RPC_H_ #define ANDROID_DVR_BUFFERHUB_RPC_H_ +#include "buffer_hub_defs.h" + #include <cutils/native_handle.h> -#include <sys/types.h> #include <ui/BufferQueueDefs.h> -#include <dvr/dvr_api.h> #include <pdx/channel_handle.h> #include <pdx/file_handle.h> #include <pdx/rpc/remote_method.h> @@ -15,71 +15,6 @@ namespace android { namespace dvr { -namespace BufferHubDefs { - -static constexpr uint32_t kMetadataFormat = HAL_PIXEL_FORMAT_BLOB; -static constexpr uint32_t kMetadataUsage = - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN; - -// Single producuer multiple (up to 63) consumers ownership signal. -// 64-bit atomic unsigned int. -// -// MSB LSB -// | | -// v v -// [P|C62|...|C1|C0] -// Gain'ed state: [0|..|0|0] -> Exclusively Writable. -// Post'ed state: [1|..|0|0] -// Acquired'ed state: [1|..|X|X] -> At least one bit is set in lower 63 bits -// Released'ed state: [0|..|X|X] -> At least one bit is set in lower 63 bits -static constexpr uint64_t kProducerStateBit = 1ULL << 63; -static constexpr uint64_t kConsumerStateMask = (1ULL << 63) - 1; - -static inline void ModifyBufferState(std::atomic<uint64_t>* buffer_state, - uint64_t clear_mask, uint64_t set_mask) { - uint64_t old_state; - uint64_t new_state; - do { - old_state = buffer_state->load(); - new_state = (old_state & ~clear_mask) | set_mask; - } while (!buffer_state->compare_exchange_weak(old_state, new_state)); -} - -static inline bool IsBufferGained(uint64_t state) { return state == 0; } - -static inline bool IsBufferPosted(uint64_t state, - uint64_t consumer_bit = kConsumerStateMask) { - return (state & kProducerStateBit) && !(state & consumer_bit); -} - -static inline bool IsBufferAcquired(uint64_t state) { - return (state & kProducerStateBit) && (state & kConsumerStateMask); -} - -static inline bool IsBufferReleased(uint64_t state) { - return !(state & kProducerStateBit) && (state & kConsumerStateMask); -} - -struct __attribute__((packed, aligned(8))) MetadataHeader { - // Internal data format, which can be updated as long as the size, padding and - // field alignment of the struct is consistent within the same ABI. As this - // part is subject for future updates, it's not stable cross Android version, - // so don't have it visible from outside of the Android platform (include Apps - // and vendor HAL). - std::atomic<uint64_t> buffer_state; - std::atomic<uint64_t> fence_state; - uint64_t queue_index; - - // Public data format, which should be updated with caution. See more details - // in dvr_api.h - DvrNativeBufferMetadata metadata; -}; - -static_assert(sizeof(MetadataHeader) == 128, "Unexpected MetadataHeader size"); -static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader); - -} // namespace BufferHubDefs - template <typename FileHandleType> class NativeBufferHandle { public: @@ -164,11 +99,12 @@ class BufferDescription { public: BufferDescription() = default; BufferDescription(const IonBuffer& buffer, const IonBuffer& metadata, int id, - uint64_t buffer_state_bit, + int buffer_cid, uint32_t client_state_mask, const FileHandleType& acquire_fence_fd, const FileHandleType& release_fence_fd) : id_(id), - buffer_state_bit_(buffer_state_bit), + buffer_cid_(buffer_cid), + client_state_mask_(client_state_mask), buffer_(buffer, id), metadata_(metadata, id), acquire_fence_fd_(acquire_fence_fd.Borrow()), @@ -177,14 +113,17 @@ class BufferDescription { BufferDescription(BufferDescription&& other) noexcept = default; BufferDescription& operator=(BufferDescription&& other) noexcept = default; - // ID of the buffer client. All BufferHubBuffer clients derived from the same - // buffer in bufferhubd share the same buffer id. + // ID of the buffer client. All BufferHub clients derived from the same buffer + // in bufferhubd share the same buffer id. int id() const { return id_; } - // State mask of the buffer client. Each BufferHubBuffer client backed by the - // same buffer channel has uniqued state bit among its siblings. For a - // producer buffer the bit must be kProducerStateBit; for a consumer the bit - // must be one of the kConsumerStateMask. - uint64_t buffer_state_bit() const { return buffer_state_bit_; } + + // Channel ID of the buffer client. Each BufferHub client has its system + // unique channel id. + int buffer_cid() const { return buffer_cid_; } + + // State mask of the buffer client. Each BufferHub client backed by the + // same buffer channel has uniqued state bit among its siblings. + uint32_t client_state_mask() const { return client_state_mask_; } FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); } FileHandleType take_release_fence() { return std::move(release_fence_fd_); } @@ -193,7 +132,8 @@ class BufferDescription { private: int id_{-1}; - uint64_t buffer_state_bit_{0}; + int buffer_cid_{-1}; + uint32_t client_state_mask_{0U}; // Two IonBuffers: one for the graphic buffer and one for metadata. NativeBufferHandle<FileHandleType> buffer_; NativeBufferHandle<FileHandleType> metadata_; @@ -202,8 +142,8 @@ class BufferDescription { FileHandleType acquire_fence_fd_; FileHandleType release_fence_fd_; - PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_, - buffer_state_bit_, buffer_, metadata_, + PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_, buffer_cid_, + client_state_mask_, buffer_, metadata_, acquire_fence_fd_, release_fence_fd_); BufferDescription(const BufferDescription&) = delete; @@ -372,19 +312,15 @@ struct BufferHubRPC { kOpProducerGain, kOpConsumerAcquire, kOpConsumerRelease, - kOpConsumerSetIgnore, - kOpProducerBufferDetach, kOpConsumerBufferDetach, - kOpDetachedBufferCreate, - kOpDetachedBufferPromote, kOpCreateProducerQueue, kOpCreateConsumerQueue, kOpGetQueueInfo, kOpProducerQueueAllocateBuffers, + kOpProducerQueueInsertBuffer, kOpProducerQueueRemoveBuffer, kOpConsumerQueueImportBuffers, // TODO(b/77153033): Separate all those RPC operations into subclasses. - kOpDetachedBufferBase = 1000, }; // Aliases. @@ -405,9 +341,6 @@ struct BufferHubRPC { PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire, LocalFence(Void)); PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease, void(LocalFence release_fence)); - PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore)); - PDX_REMOTE_METHOD(ProducerBufferDetach, kOpProducerBufferDetach, - LocalChannelHandle(Void)); // Detaches a ConsumerBuffer from an existing producer/consumer set. Can only // be called when the consumer is the only consumer and it has exclusive @@ -430,31 +363,14 @@ struct BufferHubRPC { std::vector<std::pair<LocalChannelHandle, size_t>>( uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, size_t buffer_count)); + PDX_REMOTE_METHOD(ProducerQueueInsertBuffer, kOpProducerQueueInsertBuffer, + size_t(int buffer_cid)); PDX_REMOTE_METHOD(ProducerQueueRemoveBuffer, kOpProducerQueueRemoveBuffer, void(size_t slot)); PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers, std::vector<std::pair<LocalChannelHandle, size_t>>(Void)); }; -struct DetachedBufferRPC final : public BufferHubRPC { - private: - enum { - kOpCreate = kOpDetachedBufferBase, - kOpImport, - kOpPromote, - }; - - public: - PDX_REMOTE_METHOD(Create, kOpCreate, - void(uint32_t width, uint32_t height, uint32_t layer_count, - uint32_t format, uint64_t usage, - size_t user_metadata_size)); - PDX_REMOTE_METHOD(Import, kOpImport, BufferDescription<LocalHandle>(Void)); - PDX_REMOTE_METHOD(Promote, kOpPromote, LocalChannelHandle(Void)); - - PDX_REMOTE_API(API, Create, Promote); -}; - } // namespace dvr } // namespace android diff --git a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h new file mode 100644 index 0000000000..7aa50b1985 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h @@ -0,0 +1,83 @@ +#ifndef ANDROID_DVR_CONSUMER_BUFFER_H_ +#define ANDROID_DVR_CONSUMER_BUFFER_H_ + +#include <private/dvr/buffer_hub_base.h> + +namespace android { +namespace dvr { + +// BufferConsumer was originally poorly named and gets easily confused with +// IGraphicBufferConsumer. Actually, BufferConsumer is a single buffer that can +// consume (i.e. read) data from a buffer, but it doesn't consume buffer. On +// the other hand, IGraphicBufferConsumer is the consumer end of a BufferQueue +// and it is used to consume buffers. +// +// TODO(b/116855254): Remove this typedef once rename is complete in other +// projects and/or branches. +typedef class ConsumerBuffer BufferConsumer; + +// This is a connection to a producer buffer, which can be located in another +// application. When that buffer is Post()ed, this fd will be signaled and +// Acquire allows read access. The user is responsible for making sure that +// Acquire is called with the correct metadata structure. The only guarantee the +// API currently provides is that an Acquire() with metadata of the wrong size +// will fail. +class ConsumerBuffer : public pdx::ClientBase<ConsumerBuffer, BufferHubBase> { + public: + // This call assumes ownership of |fd|. + static std::unique_ptr<ConsumerBuffer> Import(LocalChannelHandle channel); + static std::unique_ptr<ConsumerBuffer> Import( + Status<LocalChannelHandle> status); + + // Attempt to retrieve a post event from buffer hub. If successful, + // |ready_fence| will be set to a fence to wait on until the buffer is ready. + // This call will only succeed after the fd is signalled. This call may be + // performed as an alternative to the Acquire() with metadata. In such cases + // the metadata is not read. + // + // This returns zero or negative unix error code. + int Acquire(LocalHandle* ready_fence); + + // Attempt to retrieve a post event from buffer hub. If successful, + // |ready_fence| is set to a fence signaling that the contents of the buffer + // are available. This call will only succeed if the buffer is in the posted + // state. + // Returns zero on success, or a negative errno code otherwise. + int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size); + + // Asynchronously acquires a bufer. + int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); + + // Releases the buffer from any buffer state. If the fence is valid the fence + // determines the buffer usage, otherwise the buffer is released immediately. + // This returns zero or a negative unix error code. + int Release(const LocalHandle& release_fence); + int ReleaseAsync(); + + // Asynchronously releases a buffer. Similar to the synchronous version above, + // except that it does not wait for BufferHub to reply with success or error. + // The fence and metadata are passed to consumer via shared fd and shared + // memory. + int ReleaseAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence); + + // May be called after or instead of Acquire to indicate that the consumer + // does not need to access the buffer this cycle. This returns zero or a + // negative unix error code. + int Discard(); + + private: + friend BASE; + + explicit ConsumerBuffer(LocalChannelHandle channel); + + // Local state transition helpers. + int LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); + int LocalRelease(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_CONSUMER_BUFFER_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h b/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h deleted file mode 100644 index 6d0b502271..0000000000 --- a/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef ANDROID_DVR_DETACHED_BUFFER_H_ -#define ANDROID_DVR_DETACHED_BUFFER_H_ - -#include <private/dvr/buffer_hub_client.h> - -namespace android { -namespace dvr { - -class DetachedBuffer { - public: - // Allocates a standalone DetachedBuffer not associated with any producer - // consumer set. - static std::unique_ptr<DetachedBuffer> Create(uint32_t width, uint32_t height, - uint32_t layer_count, - uint32_t format, uint64_t usage, - size_t user_metadata_size) { - return std::unique_ptr<DetachedBuffer>(new DetachedBuffer( - width, height, layer_count, format, usage, user_metadata_size)); - } - - // Imports the given channel handle to a DetachedBuffer, taking ownership. - static std::unique_ptr<DetachedBuffer> Import( - pdx::LocalChannelHandle channel_handle) { - return std::unique_ptr<DetachedBuffer>( - new DetachedBuffer(std::move(channel_handle))); - } - - DetachedBuffer(const DetachedBuffer&) = delete; - void operator=(const DetachedBuffer&) = delete; - - const sp<GraphicBuffer>& buffer() const { return buffer_.buffer(); } - - int id() const { return id_; } - - // Returns true if the buffer holds an open PDX channels towards bufferhubd. - bool IsConnected() const { return client_.IsValid(); } - - // Returns true if the buffer holds an valid gralloc buffer handle that's - // availble for the client to read from and/or write into. - bool IsValid() const { return buffer_.IsValid(); } - - // Returns the event mask for all the events that are pending on this buffer - // (see sys/poll.h for all possible bits). - pdx::Status<int> GetEventMask(int events) { - if (auto* channel = client_.GetChannel()) { - return channel->GetEventMask(events); - } else { - return pdx::ErrorStatus(EINVAL); - } - } - - // Polls the fd for |timeout_ms| milliseconds (-1 for infinity). - int Poll(int timeout_ms); - - // Promotes a DetachedBuffer to become a ProducerBuffer. Once promoted the - // DetachedBuffer channel will be closed automatically on successful IPC - // return. Further IPCs towards this channel will return error. - pdx::Status<pdx::LocalChannelHandle> Promote(); - - // Takes the underlying graphic buffer out of this DetachedBuffer. This call - // immediately invalidates this DetachedBuffer object and transfers the - // underlying pdx::LocalChannelHandle into the GraphicBuffer. - sp<GraphicBuffer> TakeGraphicBuffer(); - - private: - DetachedBuffer(uint32_t width, uint32_t height, uint32_t layer_count, - uint32_t format, uint64_t usage, size_t user_metadata_size); - - DetachedBuffer(pdx::LocalChannelHandle channel_handle); - - int ImportGraphicBuffer(); - - // Global id for the buffer that is consistent across processes. - int id_; - IonBuffer buffer_; - BufferHubClient client_; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_DETACHED_BUFFER_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h index 860f08ae7a..ed38e7f448 100644 --- a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h +++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h @@ -24,7 +24,7 @@ class IonBuffer { IonBuffer& operator=(IonBuffer&& other) noexcept; // Returns check this IonBuffer holds a valid Gralloc buffer. - bool IsValid() const { return buffer_ && buffer_->initCheck() == NO_ERROR; } + bool IsValid() const { return buffer_ && buffer_->initCheck() == OK; } // Frees the underlying native handle and leaves the instance initialized to // empty. diff --git a/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h new file mode 100644 index 0000000000..a5c6ca238a --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h @@ -0,0 +1,102 @@ +#ifndef ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_ +#define ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_ + +#include <cutils/native_handle.h> +#include <log/log.h> +#include <pdx/rpc/serializable.h> + +#include <vector> + +namespace android { +namespace dvr { + +// A PDX-friendly wrapper to maintain the life cycle of a native_handle_t +// object. +// +// See https://source.android.com/devices/architecture/hidl/types#handle_t for +// more information about native_handle_t. +template <typename FileHandleType> +class NativeHandleWrapper { + public: + NativeHandleWrapper() = default; + NativeHandleWrapper(NativeHandleWrapper&& other) = default; + NativeHandleWrapper& operator=(NativeHandleWrapper&& other) = default; + + // Create a new NativeHandleWrapper by duplicating the handle. + explicit NativeHandleWrapper(const native_handle_t* handle) { + const int fd_count = handle->numFds; + const int int_count = handle->numInts; + + // Populate the fd and int vectors: native_handle->data[] is an array of fds + // followed by an array of opaque ints. + for (int i = 0; i < fd_count; i++) { + fds_.emplace_back(FileHandleType::AsDuplicate(handle->data[i])); + } + for (int i = 0; i < int_count; i++) { + ints_.push_back(handle->data[fd_count + i]); + } + } + + size_t int_count() const { return ints_.size(); } + size_t fd_count() const { return fds_.size(); } + bool IsValid() const { return ints_.size() != 0 || fds_.size() != 0; } + + // Duplicate a native handle from the wrapper. + native_handle_t* DuplicateHandle() const { + if (!IsValid()) { + return nullptr; + } + + // numFds + numInts ints. + std::vector<FileHandleType> fds; + for (const auto& fd : fds_) { + if (!fd.IsValid()) { + return nullptr; + } + fds.emplace_back(fd.Duplicate()); + } + + return FromFdsAndInts(std::move(fds), ints_); + } + + // Takes the native handle out of the wrapper. + native_handle_t* TakeHandle() { + if (!IsValid()) { + return nullptr; + } + + return FromFdsAndInts(std::move(fds_), std::move(ints_)); + } + + private: + NativeHandleWrapper(const NativeHandleWrapper&) = delete; + void operator=(const NativeHandleWrapper&) = delete; + + static native_handle_t* FromFdsAndInts(std::vector<FileHandleType> fds, + std::vector<int> ints) { + native_handle_t* handle = native_handle_create(fds.size(), ints.size()); + if (!handle) { + ALOGE("NativeHandleWrapper::TakeHandle: Failed to create new handle."); + return nullptr; + } + + // numFds + numInts ints. + for (int i = 0; i < handle->numFds; i++) { + handle->data[i] = fds[i].Release(); + } + memcpy(&handle->data[handle->numFds], ints.data(), + sizeof(int) * handle->numInts); + + return handle; + } + + std::vector<int> ints_; + std::vector<FileHandleType> fds_; + + PDX_SERIALIZABLE_MEMBERS(NativeHandleWrapper<FileHandleType>, ints_, fds_); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h new file mode 100644 index 0000000000..2761416034 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h @@ -0,0 +1,113 @@ +#ifndef ANDROID_DVR_PRODUCER_BUFFER_H_ +#define ANDROID_DVR_PRODUCER_BUFFER_H_ + +#include <private/dvr/buffer_hub_base.h> + +namespace android { +namespace dvr { + +// BufferProducer was originally poorly named and gets easily confused with +// IGraphicBufferProducer. Actually, BufferProducer is a single buffer that can +// produce (i.e. write) data into a buffer, but it doesn't produce buffer. On +// the other hand, IGraphicBufferProducer is the producer end of a BufferQueue +// and it is used to produce buffers. +// +// TODO(b/116855254): Remove this typedef once rename is complete in other +// projects and/or branches. +typedef class ProducerBuffer BufferProducer; + +// This represents a writable buffer. Calling Post notifies all clients and +// makes the buffer read-only. Call Gain to acquire write access. A buffer +// may have many consumers. +// +// The user of ProducerBuffer is responsible with making sure that the Post() is +// done with the correct metadata type and size. The user is also responsible +// for making sure that remote ends (BufferConsumers) are also using the correct +// metadata when acquiring the buffer. The API guarantees that a Post() with a +// metadata of wrong size will fail. However, it currently does not do any +// type checking. +// The API also assumes that metadata is a serializable type (plain old data). +class ProducerBuffer : public pdx::ClientBase<ProducerBuffer, BufferHubBase> { + public: + // Imports a bufferhub producer channel, assuming ownership of its handle. + static std::unique_ptr<ProducerBuffer> Import(LocalChannelHandle channel); + static std::unique_ptr<ProducerBuffer> Import( + Status<LocalChannelHandle> status); + + // Asynchronously posts a buffer. The fence and metadata are passed to + // consumer via shared fd and shared memory. + int PostAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence); + + // Post this buffer, passing |ready_fence| to the consumers. The bytes in + // |meta| are passed unaltered to the consumers. The producer must not modify + // the buffer until it is re-gained. + // This returns zero or a negative unix error code. + int Post(const LocalHandle& ready_fence, const void* meta, + size_t user_metadata_size); + + int Post(const LocalHandle& ready_fence) { + return Post(ready_fence, nullptr, 0); + } + + // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it + // must be waited on before using the buffer. If it is not valid then the + // buffer is free for immediate use. This call will succeed if the buffer + // is in the released state, or in posted state and gain_posted_buffer is + // true. + // + // @param release_fence output fence. + // @param gain_posted_buffer whether to gain posted buffer or not. + // @return This returns zero or a negative unix error code. + int Gain(LocalHandle* release_fence, bool gain_posted_buffer = false); + + // Asynchronously marks a released buffer as gained. This method is similar to + // the synchronous version above, except that it does not wait for BufferHub + // to acknowledge success or failure. Because of the asynchronous nature of + // the underlying message, no error is returned if this method is called when + // the buffer is in an incorrect state. Returns zero if sending the message + // succeeded, or a negative errno code if local error check fails. + // TODO(b/112007999): gain_posted_buffer true is only used to prevent + // libdvrtracking from starving when there are non-responding clients. This + // gain_posted_buffer param can be removed once libdvrtracking start to use + // the new AHardwareBuffer API. + int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence, + bool gain_posted_buffer = false); + int GainAsync(); + + // Detaches a ProducerBuffer from an existing producer/consumer set. Can only + // be called when a producer buffer has exclusive access to the buffer (i.e. + // in the gain'ed state). On the successful return of the IPC call, a new + // LocalChannelHandle representing a detached buffer will be returned and all + // existing producer and consumer channels will be closed. Further IPCs + // towards those channels will return error. + Status<LocalChannelHandle> Detach(); + + private: + friend BASE; + + // Constructors are automatically exposed through ProducerBuffer::Create(...) + // static template methods inherited from ClientBase, which take the same + // arguments as the constructors. + + // Constructs a buffer with the given geometry and parameters. + ProducerBuffer(uint32_t width, uint32_t height, uint32_t format, + uint64_t usage, size_t metadata_size = 0); + + // Constructs a blob (flat) buffer with the given usage flags. + ProducerBuffer(uint64_t usage, size_t size); + + // Imports the given file handle to a producer channel, taking ownership. + explicit ProducerBuffer(LocalChannelHandle channel); + + // Local state transition helpers. + int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence, + bool gain_posted_buffer = false); + int LocalPost(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_PRODUCER_BUFFER_H_ diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp index 129553128e..196541010e 100644 --- a/libs/vr/libbufferhub/ion_buffer.cpp +++ b/libs/vr/libbufferhub/ion_buffer.cpp @@ -205,7 +205,7 @@ int IonBuffer::Lock(uint32_t usage, int x, int y, int width, int height, status_t err = buffer_->lock(usage, Rect(x, y, x + width, y + height), address); - if (err != NO_ERROR) + if (err != OK) return -EINVAL; else return 0; @@ -220,7 +220,7 @@ int IonBuffer::LockYUV(uint32_t usage, int x, int y, int width, int height, status_t err = buffer_->lockYCbCr(usage, Rect(x, y, x + width, y + height), yuv); - if (err != NO_ERROR) + if (err != OK) return -EINVAL; else return 0; @@ -231,7 +231,7 @@ int IonBuffer::Unlock() { ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle()); status_t err = buffer_->unlock(); - if (err != NO_ERROR) + if (err != OK) return -EINVAL; else return 0; diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp new file mode 100644 index 0000000000..5274bf25d5 --- /dev/null +++ b/libs/vr/libbufferhub/producer_buffer.cpp @@ -0,0 +1,311 @@ +#include <private/dvr/producer_buffer.h> + +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Status; + +namespace android { +namespace dvr { + +ProducerBuffer::ProducerBuffer(uint32_t width, uint32_t height, uint32_t format, + uint64_t usage, size_t user_metadata_size) + : BASE(BufferHubRPC::kClientPath) { + ATRACE_NAME("ProducerBuffer::ProducerBuffer"); + ALOGD_IF(TRACE, + "ProducerBuffer::ProducerBuffer: fd=%d width=%u height=%u format=%u " + "usage=%" PRIx64 " user_metadata_size=%zu", + event_fd(), width, height, format, usage, user_metadata_size); + + auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( + width, height, format, usage, user_metadata_size); + if (!status) { + ALOGE( + "ProducerBuffer::ProducerBuffer: Failed to create producer buffer: %s", + status.GetErrorMessage().c_str()); + Close(-status.error()); + return; + } + + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +ProducerBuffer::ProducerBuffer(uint64_t usage, size_t size) + : BASE(BufferHubRPC::kClientPath) { + ATRACE_NAME("ProducerBuffer::ProducerBuffer"); + ALOGD_IF(TRACE, "ProducerBuffer::ProducerBuffer: usage=%" PRIx64 " size=%zu", + usage, size); + const int width = static_cast<int>(size); + const int height = 1; + const int format = HAL_PIXEL_FORMAT_BLOB; + const size_t user_metadata_size = 0; + + auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( + width, height, format, usage, user_metadata_size); + if (!status) { + ALOGE("ProducerBuffer::ProducerBuffer: Failed to create blob: %s", + status.GetErrorMessage().c_str()); + Close(-status.error()); + return; + } + + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +ProducerBuffer::ProducerBuffer(LocalChannelHandle channel) + : BASE(std::move(channel)) { + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +int ProducerBuffer::LocalPost(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence) { + if (const int error = CheckMetadata(meta->user_metadata_size)) + return error; + + // The buffer can be posted iff the buffer state for this client is gained. + uint32_t current_buffer_state = + buffer_state_->load(std::memory_order_acquire); + if (!BufferHubDefs::IsClientGained(current_buffer_state, + client_state_mask())) { + ALOGE("%s: not gained, id=%d state=%" PRIx32 ".", __FUNCTION__, id(), + current_buffer_state); + return -EBUSY; + } + + // Set the producer client buffer state to released, other clients' buffer + // state to posted. + uint32_t current_active_clients_bit_mask = + active_clients_bit_mask_->load(std::memory_order_acquire); + uint32_t updated_buffer_state = current_active_clients_bit_mask & + (~client_state_mask()) & + BufferHubDefs::kHighBitsMask; + while (!buffer_state_->compare_exchange_weak( + current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, + std::memory_order_acquire)) { + ALOGD( + "%s: Failed to post the buffer. Current buffer state was changed to " + "%" PRIx32 + " when trying to post the buffer and modify the buffer state to " + "%" PRIx32 + ". About to try again if the buffer is still gained by this client.", + __FUNCTION__, current_buffer_state, updated_buffer_state); + if (!BufferHubDefs::IsClientGained(current_buffer_state, + client_state_mask())) { + ALOGE( + "%s: Failed to post the buffer. The buffer is no longer gained, " + "id=%d state=%" PRIx32 ".", + __FUNCTION__, id(), current_buffer_state); + return -EBUSY; + } + } + + // Copy the canonical metadata. + void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata); + memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata)); + // Copy extra user requested metadata. + if (meta->user_metadata_ptr && meta->user_metadata_size) { + void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr); + memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); + } + + // Send out the acquire fence through the shared epoll fd. Note that during + // posting no consumer is not expected to be polling on the fence. + if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_)) + return error; + + return 0; +} + +int ProducerBuffer::Post(const LocalHandle& ready_fence, const void* meta, + size_t user_metadata_size) { + ATRACE_NAME("ProducerBuffer::Post"); + + // Populate cononical metadata for posting. + DvrNativeBufferMetadata canonical_meta; + canonical_meta.user_metadata_ptr = reinterpret_cast<uint64_t>(meta); + canonical_meta.user_metadata_size = user_metadata_size; + + if (const int error = LocalPost(&canonical_meta, ready_fence)) + return error; + + return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>( + BorrowedFence(ready_fence.Borrow()))); +} + +int ProducerBuffer::PostAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence) { + ATRACE_NAME("ProducerBuffer::PostAsync"); + + if (const int error = LocalPost(meta, ready_fence)) + return error; + + return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode)); +} + +int ProducerBuffer::LocalGain(DvrNativeBufferMetadata* out_meta, + LocalHandle* out_fence, bool gain_posted_buffer) { + if (!out_meta) + return -EINVAL; + + uint32_t current_buffer_state = + buffer_state_->load(std::memory_order_acquire); + ALOGD_IF(TRACE, "%s: buffer=%d, state=%" PRIx32 ".", __FUNCTION__, id(), + current_buffer_state); + + if (BufferHubDefs::IsClientGained(current_buffer_state, + client_state_mask())) { + ALOGV("%s: already gained id=%d.", __FUNCTION__, id()); + return 0; + } + if (BufferHubDefs::AnyClientAcquired(current_buffer_state) || + BufferHubDefs::AnyClientGained(current_buffer_state) || + (BufferHubDefs::AnyClientPosted(current_buffer_state) && + !gain_posted_buffer)) { + ALOGE("%s: not released id=%d state=%" PRIx32 ".", __FUNCTION__, id(), + current_buffer_state); + return -EBUSY; + } + // Change the buffer state to gained state. + uint32_t updated_buffer_state = client_state_mask(); + while (!buffer_state_->compare_exchange_weak( + current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, + std::memory_order_acquire)) { + ALOGD( + "%s: Failed to gain the buffer. Current buffer state was changed to " + "%" PRIx32 + " when trying to gain the buffer and modify the buffer state to " + "%" PRIx32 + ". About to try again if the buffer is still not read by other " + "clients.", + __FUNCTION__, current_buffer_state, updated_buffer_state); + + if (BufferHubDefs::AnyClientAcquired(current_buffer_state) || + BufferHubDefs::AnyClientGained(current_buffer_state) || + (BufferHubDefs::AnyClientPosted(current_buffer_state) && + !gain_posted_buffer)) { + ALOGE( + "%s: Failed to gain the buffer. The buffer is no longer released. " + "id=%d state=%" PRIx32 ".", + __FUNCTION__, id(), current_buffer_state); + return -EBUSY; + } + } + + // Canonical metadata is undefined on Gain. Except for user_metadata and + // release_fence_mask. Fill in the user_metadata_ptr in address space of the + // local process. + if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) { + out_meta->user_metadata_size = + metadata_header_->metadata.user_metadata_size; + out_meta->user_metadata_ptr = + reinterpret_cast<uint64_t>(user_metadata_ptr_); + } else { + out_meta->user_metadata_size = 0; + out_meta->user_metadata_ptr = 0; + } + + uint32_t current_fence_state = fence_state_->load(std::memory_order_acquire); + uint32_t current_active_clients_bit_mask = + active_clients_bit_mask_->load(std::memory_order_acquire); + // If there are release fence(s) from consumer(s), we need to return it to the + // consumer(s). + // TODO(b/112007999) add an atomic variable in metadata header in shared + // memory to indicate which client is the last producer of the buffer. + // Currently, assume the first client is the only producer to the buffer. + if (current_fence_state & current_active_clients_bit_mask & + (~BufferHubDefs::kFirstClientBitMask)) { + *out_fence = shared_release_fence_.Duplicate(); + out_meta->release_fence_mask = current_fence_state & + current_active_clients_bit_mask & + (~BufferHubDefs::kFirstClientBitMask); + } + + return 0; +} + +int ProducerBuffer::Gain(LocalHandle* release_fence, bool gain_posted_buffer) { + ATRACE_NAME("ProducerBuffer::Gain"); + + DvrNativeBufferMetadata meta; + if (const int error = LocalGain(&meta, release_fence, gain_posted_buffer)) + return error; + + auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>(); + if (!status) + return -status.error(); + return 0; +} + +int ProducerBuffer::GainAsync(DvrNativeBufferMetadata* out_meta, + LocalHandle* release_fence, + bool gain_posted_buffer) { + ATRACE_NAME("ProducerBuffer::GainAsync"); + + if (const int error = LocalGain(out_meta, release_fence, gain_posted_buffer)) + return error; + + return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode)); +} + +int ProducerBuffer::GainAsync() { + DvrNativeBufferMetadata meta; + LocalHandle fence; + return GainAsync(&meta, &fence); +} + +std::unique_ptr<ProducerBuffer> ProducerBuffer::Import( + LocalChannelHandle channel) { + ALOGD_IF(TRACE, "ProducerBuffer::Import: channel=%d", channel.value()); + return ProducerBuffer::Create(std::move(channel)); +} + +std::unique_ptr<ProducerBuffer> ProducerBuffer::Import( + Status<LocalChannelHandle> status) { + return Import(status ? status.take() + : LocalChannelHandle{nullptr, -status.error()}); +} + +Status<LocalChannelHandle> ProducerBuffer::Detach() { + // TODO(b/112338294) remove after migrate producer buffer to binder + ALOGW("ProducerBuffer::Detach: not supported operation during migration"); + return {}; + + // TODO(b/112338294) Keep here for reference. Remove it after new logic is + // written. + /* uint32_t buffer_state = buffer_state_->load(std::memory_order_acquire); + if (!BufferHubDefs::IsClientGained( + buffer_state, BufferHubDefs::kFirstClientStateMask)) { + // Can only detach a ProducerBuffer when it's in gained state. + ALOGW("ProducerBuffer::Detach: The buffer (id=%d, state=0x%" PRIx32 + ") is not in gained state.", + id(), buffer_state); + return {}; + } + + Status<LocalChannelHandle> status = + InvokeRemoteMethod<BufferHubRPC::ProducerBufferDetach>(); + ALOGE_IF(!status, + "ProducerBuffer::Detach: Failed to detach buffer (id=%d): %s.", id(), + status.GetErrorMessage().c_str()); + return status; */ +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp index 9f72c05f0c..20894e3588 100644 --- a/libs/vr/libbufferhubqueue/Android.bp +++ b/libs/vr/libbufferhubqueue/Android.bp @@ -59,6 +59,9 @@ cc_library_shared { static_libs: staticLibraries, shared_libs: sharedLibraries, header_libs: headerLibraries, + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, } subdirs = ["benchmarks", "tests"] diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp index 5089b8754e..ef1eed6d0a 100644 --- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp +++ b/libs/vr/libbufferhubqueue/benchmarks/Android.bp @@ -5,7 +5,7 @@ cc_benchmark { "libbase", "libbinder", "libcutils", - "libdvr", + "libdvr.google", "libgui", "liblog", "libhardware", diff --git a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp index 4ca8671f24..b6813eb51d 100644 --- a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp +++ b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp @@ -66,7 +66,7 @@ class BufferTransportService : public BBinder { reply->writeStrongBinder( IGraphicBufferProducer::asBinder(new_queue->producer)); buffer_queues_.push_back(new_queue); - return NO_ERROR; + return OK; } default: return UNKNOWN_TRANSACTION; @@ -89,7 +89,7 @@ class BufferTransportService : public BBinder { /*waitForFence=*/false); } - if (ret != NO_ERROR) { + if (ret != OK) { LOG(ERROR) << "Failed to acquire next buffer."; return; } @@ -99,7 +99,7 @@ class BufferTransportService : public BBinder { ret = buffer_item_consumer_->releaseBuffer(buffer); } - if (ret != NO_ERROR) { + if (ret != OK) { LOG(ERROR) << "Failed to release buffer."; return; } @@ -171,14 +171,14 @@ class BinderBufferTransport : public BufferTransport { Parcel data; Parcel reply; int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply); - if (error != NO_ERROR) { + if (error != OK) { LOG(ERROR) << "Failed to get buffer queue over binder."; return nullptr; } sp<IBinder> binder; error = reply.readNullableStrongBinder(&binder); - if (error != NO_ERROR) { + if (error != OK) { LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder."; return nullptr; } @@ -206,7 +206,7 @@ class BinderBufferTransport : public BufferTransport { class DvrApi { public: DvrApi() { - handle_ = dlopen("libdvr.so", RTLD_NOW | RTLD_LOCAL); + handle_ = dlopen("libdvr.google.so", RTLD_NOW | RTLD_LOCAL); CHECK(handle_); auto dvr_get_api = diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp index 8feb1cd803..9c4f73f37a 100644 --- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp +++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp @@ -69,7 +69,7 @@ void BufferHubQueue::Initialize() { .data = {.u64 = Stuff(-1, BufferHubQueue::kEpollQueueEventIndex)}}; ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_fd(), &event); if (ret < 0) { - ALOGE("BufferHubQueue::Initialize: Failed to add event fd to epoll set: %s", + ALOGE("%s: Failed to add event fd to epoll set: %s", __FUNCTION__, strerror(-ret)); } } @@ -77,7 +77,7 @@ void BufferHubQueue::Initialize() { Status<void> BufferHubQueue::ImportQueue() { auto status = InvokeRemoteMethod<BufferHubRPC::GetQueueInfo>(); if (!status) { - ALOGE("BufferHubQueue::ImportQueue: Failed to import queue: %s", + ALOGE("%s: Failed to import queue: %s", __FUNCTION__, status.GetErrorMessage().c_str()); return ErrorStatus(status.error()); } else { @@ -136,9 +136,7 @@ BufferHubQueue::CreateConsumerQueueParcelable(bool silent) { consumer_queue->GetChannel()->TakeChannelParcelable()); if (!queue_parcelable.IsValid()) { - ALOGE( - "BufferHubQueue::CreateConsumerQueueParcelable: Failed to create " - "consumer queue parcelable."); + ALOGE("%s: Failed to create consumer queue parcelable.", __FUNCTION__); return ErrorStatus(EINVAL); } @@ -169,8 +167,7 @@ bool BufferHubQueue::WaitForBuffers(int timeout) { } if (ret < 0 && ret != -EINTR) { - ALOGE("BufferHubQueue::WaitForBuffers: Failed to wait for buffers: %s", - strerror(-ret)); + ALOGE("%s: Failed to wait for buffers: %s", __FUNCTION__, strerror(-ret)); return false; } @@ -264,27 +261,26 @@ Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) { // wait will be tried again to acquire the newly imported buffer. auto buffer_status = OnBufferAllocated(); if (!buffer_status) { - ALOGE("BufferHubQueue::HandleQueueEvent: Failed to import buffer: %s", + ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, buffer_status.GetErrorMessage().c_str()); } } else if (events & EPOLLHUP) { - ALOGD_IF(TRACE, "BufferHubQueue::HandleQueueEvent: hang up event!"); + ALOGD_IF(TRACE, "%s: hang up event!", __FUNCTION__); hung_up_ = true; } else { - ALOGW("BufferHubQueue::HandleQueueEvent: Unknown epoll events=%x", events); + ALOGW("%s: Unknown epoll events=%x", __FUNCTION__, events); } return {}; } Status<void> BufferHubQueue::AddBuffer( - const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) { - ALOGD_IF(TRACE, "BufferHubQueue::AddBuffer: buffer_id=%d slot=%zu", - buffer->id(), slot); + const std::shared_ptr<BufferHubBase>& buffer, size_t slot) { + ALOGD_IF(TRACE, "%s: buffer_id=%d slot=%zu", __FUNCTION__, buffer->id(), + slot); if (is_full()) { - ALOGE("BufferHubQueue::AddBuffer queue is at maximum capacity: %zu", - capacity_); + ALOGE("%s: queue is at maximum capacity: %zu", __FUNCTION__, capacity_); return ErrorStatus(E2BIG); } @@ -303,7 +299,7 @@ Status<void> BufferHubQueue::AddBuffer( const int ret = epoll_fd_.Control(EPOLL_CTL_ADD, event_source.event_fd, &event); if (ret < 0) { - ALOGE("BufferHubQueue::AddBuffer: Failed to add buffer to epoll set: %s", + ALOGE("%s: Failed to add buffer to epoll set: %s", __FUNCTION__, strerror(-ret)); return ErrorStatus(-ret); } @@ -315,17 +311,15 @@ Status<void> BufferHubQueue::AddBuffer( } Status<void> BufferHubQueue::RemoveBuffer(size_t slot) { - ALOGD_IF(TRACE, "BufferHubQueue::RemoveBuffer: slot=%zu", slot); + ALOGD_IF(TRACE, "%s: slot=%zu", __FUNCTION__, slot); if (buffers_[slot]) { for (const auto& event_source : buffers_[slot]->GetEventSources()) { const int ret = epoll_fd_.Control(EPOLL_CTL_DEL, event_source.event_fd, nullptr); if (ret < 0) { - ALOGE( - "BufferHubQueue::RemoveBuffer: Failed to remove buffer from epoll " - "set: %s", - strerror(-ret)); + ALOGE("%s: Failed to remove buffer from epoll set: %s", __FUNCTION__, + strerror(-ret)); return ErrorStatus(-ret); } } @@ -343,6 +337,15 @@ Status<void> BufferHubQueue::RemoveBuffer(size_t slot) { Status<void> BufferHubQueue::Enqueue(Entry entry) { if (!is_full()) { + // Find and remove the enqueued buffer from unavailable_buffers_slot if + // exist. + auto enqueued_buffer_iter = std::find_if( + unavailable_buffers_slot_.begin(), unavailable_buffers_slot_.end(), + [&entry](size_t slot) -> bool { return slot == entry.slot; }); + if (enqueued_buffer_iter != unavailable_buffers_slot_.end()) { + unavailable_buffers_slot_.erase(enqueued_buffer_iter); + } + available_buffers_.push(std::move(entry)); // Trigger OnBufferAvailable callback if registered. @@ -351,17 +354,16 @@ Status<void> BufferHubQueue::Enqueue(Entry entry) { return {}; } else { - ALOGE("BufferHubQueue::Enqueue: Buffer queue is full!"); + ALOGE("%s: Buffer queue is full!", __FUNCTION__); return ErrorStatus(E2BIG); } } -Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(int timeout, - size_t* slot) { - ALOGD_IF(TRACE, "BufferHubQueue::Dequeue: count=%zu, timeout=%d", count(), - timeout); +Status<std::shared_ptr<BufferHubBase>> BufferHubQueue::Dequeue(int timeout, + size_t* slot) { + ALOGD_IF(TRACE, "%s: count=%zu, timeout=%d", __FUNCTION__, count(), timeout); - PDX_TRACE_FORMAT("BufferHubQueue::Dequeue|count=%zu|", count()); + PDX_TRACE_FORMAT("%s|count=%zu|", __FUNCTION__, count()); if (count() == 0) { if (!WaitForBuffers(timeout)) @@ -372,10 +374,11 @@ Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(int timeout, PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(), entry.slot); - std::shared_ptr<BufferHubBuffer> buffer = std::move(entry.buffer); + std::shared_ptr<BufferHubBase> buffer = std::move(entry.buffer); *slot = entry.slot; available_buffers_.pop(); + unavailable_buffers_slot_.push_back(*slot); return {std::move(buffer)}; } @@ -440,6 +443,10 @@ ProducerQueue::ProducerQueue(const ProducerQueueConfig& config, Status<std::vector<size_t>> ProducerQueue::AllocateBuffers( uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, size_t buffer_count) { + if (buffer_count == 0) { + return {std::vector<size_t>()}; + } + if (capacity() + buffer_count > kMaxQueueCapacity) { ALOGE( "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot " @@ -481,10 +488,13 @@ Status<std::vector<size_t>> ProducerQueue::AllocateBuffers( } } - if (buffer_slots.size() == 0) { - // Error out if no buffer is allocated and improted. - ALOGE_IF(TRACE, "ProducerQueue::AllocateBuffers: no buffer allocated."); - ErrorStatus(ENOMEM); + if (buffer_slots.size() != buffer_count) { + // Error out if the count of imported buffer(s) is not correct. + ALOGE( + "ProducerQueue::AllocateBuffers: requested to import %zu " + "buffers, but actually imported %zu buffers.", + buffer_count, buffer_slots.size()); + return ErrorStatus(ENOMEM); } return {std::move(buffer_slots)}; @@ -503,11 +513,6 @@ Status<size_t> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height, return status.error_status(); } - if (status.get().size() == 0) { - ALOGE_IF(TRACE, "ProducerQueue::AllocateBuffer: no buffer allocated."); - ErrorStatus(ENOMEM); - } - return {status.get()[0]}; } @@ -524,11 +529,46 @@ Status<void> ProducerQueue::AddBuffer( return BufferHubQueue::Enqueue({buffer, slot, 0ULL}); } +Status<size_t> ProducerQueue::InsertBuffer( + const std::shared_ptr<BufferProducer>& buffer) { + if (buffer == nullptr || + !BufferHubDefs::IsClientGained(buffer->buffer_state(), + buffer->client_state_mask())) { + ALOGE( + "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in " + "gained state."); + return ErrorStatus(EINVAL); + } + + auto status_or_slot = + InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>( + buffer->cid()); + if (!status_or_slot) { + ALOGE( + "ProducerQueue::InsertBuffer: Failed to insert producer buffer: " + "buffer_cid=%d, error: %s.", + buffer->cid(), status_or_slot.GetErrorMessage().c_str()); + return status_or_slot.error_status(); + } + + size_t slot = status_or_slot.get(); + + // Note that we are calling AddBuffer() from the base class to explicitly + // avoid Enqueue() the BufferProducer. + auto status = BufferHubQueue::AddBuffer(buffer, slot); + if (!status) { + ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.", + status.GetErrorMessage().c_str()); + return status.error_status(); + } + return {slot}; +} + Status<void> ProducerQueue::RemoveBuffer(size_t slot) { auto status = InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot); if (!status) { - ALOGE("ProducerQueue::RemoveBuffer: Failed to remove producer buffer: %s", + ALOGE("%s: Failed to remove producer buffer: %s", __FUNCTION__, status.GetErrorMessage().c_str()); return status.error_status(); } @@ -544,31 +584,81 @@ Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue( pdx::Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue( int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, - pdx::LocalHandle* release_fence) { + pdx::LocalHandle* release_fence, bool gain_posted_buffer) { ATRACE_NAME("ProducerQueue::Dequeue"); if (slot == nullptr || out_meta == nullptr || release_fence == nullptr) { - ALOGE("ProducerQueue::Dequeue: Invalid parameter."); + ALOGE("%s: Invalid parameter.", __FUNCTION__); return ErrorStatus(EINVAL); } - auto status = BufferHubQueue::Dequeue(timeout, slot); - if (!status) - return status.error_status(); - - auto buffer = std::static_pointer_cast<BufferProducer>(status.take()); - const int ret = buffer->GainAsync(out_meta, release_fence); + std::shared_ptr<BufferProducer> buffer; + Status<std::shared_ptr<BufferHubBase>> dequeue_status = + BufferHubQueue::Dequeue(timeout, slot); + if (dequeue_status.ok()) { + buffer = std::static_pointer_cast<BufferProducer>(dequeue_status.take()); + } else { + if (gain_posted_buffer) { + Status<std::shared_ptr<BufferProducer>> dequeue_unacquired_status = + ProducerQueue::DequeueUnacquiredBuffer(slot); + if (!dequeue_unacquired_status.ok()) { + ALOGE("%s: DequeueUnacquiredBuffer returned error: %d", __FUNCTION__, + dequeue_unacquired_status.error()); + return dequeue_unacquired_status.error_status(); + } + buffer = dequeue_unacquired_status.take(); + } else { + return dequeue_status.error_status(); + } + } + const int ret = + buffer->GainAsync(out_meta, release_fence, gain_posted_buffer); if (ret < 0 && ret != -EALREADY) return ErrorStatus(-ret); return {std::move(buffer)}; } +Status<std::shared_ptr<BufferProducer>> ProducerQueue::DequeueUnacquiredBuffer( + size_t* slot) { + if (unavailable_buffers_slot_.size() < 1) { + ALOGE( + "%s: Failed to dequeue un-acquired buffer. All buffer(s) are in " + "acquired state if exist.", + __FUNCTION__); + return ErrorStatus(ENOMEM); + } + + // Find the first buffer that is not in acquired state from + // unavailable_buffers_slot_. + for (auto iter = unavailable_buffers_slot_.begin(); + iter != unavailable_buffers_slot_.end(); iter++) { + std::shared_ptr<BufferProducer> buffer = ProducerQueue::GetBuffer(*iter); + if (buffer == nullptr) { + ALOGE("%s failed. Buffer slot %d is null.", __FUNCTION__, + static_cast<int>(*slot)); + return ErrorStatus(EIO); + } + if (!BufferHubDefs::AnyClientAcquired(buffer->buffer_state())) { + *slot = *iter; + unavailable_buffers_slot_.erase(iter); + unavailable_buffers_slot_.push_back(*slot); + ALOGD("%s: Producer queue dequeue unacquired buffer in slot %d", + __FUNCTION__, static_cast<int>(*slot)); + return {std::move(buffer)}; + } + } + ALOGE( + "%s: Failed to dequeue un-acquired buffer. No un-acquired buffer exist.", + __FUNCTION__); + return ErrorStatus(EBUSY); +} + pdx::Status<ProducerQueueParcelable> ProducerQueue::TakeAsParcelable() { if (capacity() != 0) { ALOGE( - "ProducerQueue::TakeAsParcelable: producer queue can only be taken out" - " as a parcelable when empty. Current queue capacity: %zu", - capacity()); + "%s: producer queue can only be taken out as a parcelable when empty. " + "Current queue capacity: %zu", + __FUNCTION__, capacity()); return ErrorStatus(EINVAL); } @@ -592,17 +682,16 @@ ConsumerQueue::ConsumerQueue(LocalChannelHandle handle) : BufferHubQueue(std::move(handle)) { auto status = ImportQueue(); if (!status) { - ALOGE("ConsumerQueue::ConsumerQueue: Failed to import queue: %s", + ALOGE("%s: Failed to import queue: %s", __FUNCTION__, status.GetErrorMessage().c_str()); Close(-status.error()); } auto import_status = ImportBuffers(); if (import_status) { - ALOGI("ConsumerQueue::ConsumerQueue: Imported %zu buffers.", - import_status.get()); + ALOGI("%s: Imported %zu buffers.", __FUNCTION__, import_status.get()); } else { - ALOGE("ConsumerQueue::ConsumerQueue: Failed to import buffers: %s", + ALOGE("%s: Failed to import buffers: %s", __FUNCTION__, import_status.GetErrorMessage().c_str()); } } @@ -611,14 +700,11 @@ Status<size_t> ConsumerQueue::ImportBuffers() { auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerQueueImportBuffers>(); if (!status) { if (status.error() == EBADR) { - ALOGI( - "ConsumerQueue::ImportBuffers: Queue is silent, no buffers " - "imported."); + ALOGI("%s: Queue is silent, no buffers imported.", __FUNCTION__); return {0}; } else { - ALOGE( - "ConsumerQueue::ImportBuffers: Failed to import consumer buffer: %s", - status.GetErrorMessage().c_str()); + ALOGE("%s: Failed to import consumer buffer: %s", __FUNCTION__, + status.GetErrorMessage().c_str()); return status.error_status(); } } @@ -629,13 +715,13 @@ Status<size_t> ConsumerQueue::ImportBuffers() { auto buffer_handle_slots = status.take(); for (auto& buffer_handle_slot : buffer_handle_slots) { - ALOGD_IF(TRACE, "ConsumerQueue::ImportBuffers: buffer_handle=%d", + ALOGD_IF(TRACE, ": buffer_handle=%d", __FUNCTION__, buffer_handle_slot.first.value()); std::unique_ptr<BufferConsumer> buffer_consumer = BufferConsumer::Import(std::move(buffer_handle_slot.first)); if (!buffer_consumer) { - ALOGE("ConsumerQueue::ImportBuffers: Failed to import buffer: slot=%zu", + ALOGE("%s: Failed to import buffer: slot=%zu", __FUNCTION__, buffer_handle_slot.second); last_error = ErrorStatus(EPIPE); continue; @@ -644,7 +730,7 @@ Status<size_t> ConsumerQueue::ImportBuffers() { auto add_status = AddBuffer(std::move(buffer_consumer), buffer_handle_slot.second); if (!add_status) { - ALOGE("ConsumerQueue::ImportBuffers: Failed to add buffer: %s", + ALOGE("%s: Failed to add buffer: %s", __FUNCTION__, add_status.GetErrorMessage().c_str()); last_error = add_status; } else { @@ -660,8 +746,8 @@ Status<size_t> ConsumerQueue::ImportBuffers() { Status<void> ConsumerQueue::AddBuffer( const std::shared_ptr<BufferConsumer>& buffer, size_t slot) { - ALOGD_IF(TRACE, "ConsumerQueue::AddBuffer: queue_id=%d buffer_id=%d slot=%zu", - id(), buffer->id(), slot); + ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu", __FUNCTION__, id(), + buffer->id(), slot); return BufferHubQueue::AddBuffer(buffer, slot); } @@ -670,9 +756,9 @@ Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( LocalHandle* acquire_fence) { if (user_metadata_size != user_metadata_size_) { ALOGE( - "ConsumerQueue::Dequeue: Metadata size (%zu) for the dequeuing buffer " - "does not match metadata size (%zu) for the queue.", - user_metadata_size, user_metadata_size_); + "%s: Metadata size (%zu) for the dequeuing buffer does not match " + "metadata size (%zu) for the queue.", + __FUNCTION__, user_metadata_size, user_metadata_size_); return ErrorStatus(EINVAL); } @@ -687,7 +773,7 @@ Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( if (metadata_src) { memcpy(meta, metadata_src, user_metadata_size); } else { - ALOGW("ConsumerQueue::Dequeue: no user-defined metadata."); + ALOGW("%s: no user-defined metadata.", __FUNCTION__); } } @@ -699,7 +785,7 @@ Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( pdx::LocalHandle* acquire_fence) { ATRACE_NAME("ConsumerQueue::Dequeue"); if (slot == nullptr || out_meta == nullptr || acquire_fence == nullptr) { - ALOGE("ConsumerQueue::Dequeue: Invalid parameter."); + ALOGE("%s: Invalid parameter.", __FUNCTION__); return ErrorStatus(EINVAL); } @@ -716,19 +802,18 @@ Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( } Status<void> ConsumerQueue::OnBufferAllocated() { - ALOGD_IF(TRACE, "ConsumerQueue::OnBufferAllocated: queue_id=%d", id()); + ALOGD_IF(TRACE, "%s: queue_id=%d", __FUNCTION__, id()); auto status = ImportBuffers(); if (!status) { - ALOGE("ConsumerQueue::OnBufferAllocated: Failed to import buffers: %s", + ALOGE("%s: Failed to import buffers: %s", __FUNCTION__, status.GetErrorMessage().c_str()); return ErrorStatus(status.error()); } else if (status.get() == 0) { - ALOGW("ConsumerQueue::OnBufferAllocated: No new buffers allocated!"); + ALOGW("%s: No new buffers allocated!", __FUNCTION__); return ErrorStatus(ENOBUFS); } else { - ALOGD_IF(TRACE, - "ConsumerQueue::OnBufferAllocated: Imported %zu consumer buffers.", + ALOGD_IF(TRACE, "%s: Imported %zu consumer buffers.", __FUNCTION__, status.get()); return {}; } diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp index 2cd7c452be..f705749243 100644 --- a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp +++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp @@ -35,7 +35,7 @@ status_t BufferHubQueueParcelable<Magic>::writeToParcel(Parcel* parcel) const { } status_t res = parcel->writeUint32(Magic); - if (res != NO_ERROR) { + if (res != OK) { ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic."); return res; } @@ -53,10 +53,10 @@ status_t BufferHubQueueParcelable<Magic>::readFromParcel(const Parcel* parcel) { } uint32_t out_magic = 0; - status_t res = NO_ERROR; + status_t res = OK; res = parcel->readUint32(&out_magic); - if (res != NO_ERROR) + if (res != OK) return res; if (out_magic != Magic) { diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h index 60e1c4b8a9..53ab2b2e7a 100644 --- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h +++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h @@ -13,10 +13,11 @@ // in these headers and their dependencies. #include <pdx/client.h> #include <pdx/status.h> -#include <private/dvr/buffer_hub_client.h> #include <private/dvr/buffer_hub_queue_parcelable.h> #include <private/dvr/bufferhub_rpc.h> +#include <private/dvr/consumer_buffer.h> #include <private/dvr/epoll_file_descriptor.h> +#include <private/dvr/producer_buffer.h> #if defined(__clang__) #pragma clang diagnostic pop @@ -31,13 +32,13 @@ namespace dvr { class ConsumerQueue; -// |BufferHubQueue| manages a queue of |BufferHubBuffer|s. Buffers are +// |BufferHubQueue| manages a queue of |BufferHubBase|s. Buffers are // automatically re-requeued when released by the remote side. class BufferHubQueue : public pdx::Client { public: using BufferAvailableCallback = std::function<void()>; using BufferRemovedCallback = - std::function<void(const std::shared_ptr<BufferHubBuffer>&)>; + std::function<void(const std::shared_ptr<BufferHubBase>&)>; virtual ~BufferHubQueue() {} @@ -57,10 +58,10 @@ class BufferHubQueue : public pdx::Client { uint32_t default_width() const { return default_width_; } // Returns the default buffer height of this buffer queue. - uint32_t default_height() const { return static_cast<uint32_t>(default_height_); } + uint32_t default_height() const { return default_height_; } // Returns the default buffer format of this buffer queue. - uint32_t default_format() const { return static_cast<uint32_t>(default_format_); } + uint32_t default_format() const { return default_format_; } // Creates a new consumer in handle form for immediate transport over RPC. pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle( @@ -93,7 +94,7 @@ class BufferHubQueue : public pdx::Client { : -1; } - std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const { + std::shared_ptr<BufferHubBase> GetBuffer(size_t slot) const { return buffers_[slot]; } @@ -142,7 +143,7 @@ class BufferHubQueue : public pdx::Client { // Register a buffer for management by the queue. Used by subclasses to add a // buffer to internal bookkeeping. - pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBuffer>& buffer, + pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBase>& buffer, size_t slot); // Called by ProducerQueue::RemoveBuffer and ConsumerQueue::RemoveBuffer only @@ -158,8 +159,8 @@ class BufferHubQueue : public pdx::Client { // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely, // while specifying a timeout equal to zero cause Dequeue() to return // immediately, even if no buffers are available. - pdx::Status<std::shared_ptr<BufferHubBuffer>> Dequeue(int timeout, - size_t* slot); + pdx::Status<std::shared_ptr<BufferHubBase>> Dequeue(int timeout, + size_t* slot); // Waits for buffers to become available and adds them to the available queue. bool WaitForBuffers(int timeout); @@ -172,10 +173,10 @@ class BufferHubQueue : public pdx::Client { // per-buffer data. struct Entry { Entry() : slot(0) {} - Entry(const std::shared_ptr<BufferHubBuffer>& in_buffer, size_t in_slot, + Entry(const std::shared_ptr<BufferHubBase>& in_buffer, size_t in_slot, uint64_t in_index) : buffer(in_buffer), slot(in_slot), index(in_index) {} - Entry(const std::shared_ptr<BufferHubBuffer>& in_buffer, + Entry(const std::shared_ptr<BufferHubBase>& in_buffer, std::unique_ptr<uint8_t[]> in_metadata, pdx::LocalHandle in_fence, size_t in_slot) : buffer(in_buffer), @@ -185,7 +186,7 @@ class BufferHubQueue : public pdx::Client { Entry(Entry&&) = default; Entry& operator=(Entry&&) = default; - std::shared_ptr<BufferHubBuffer> buffer; + std::shared_ptr<BufferHubBase> buffer; std::unique_ptr<uint8_t[]> metadata; pdx::LocalHandle fence; size_t slot; @@ -208,6 +209,14 @@ class BufferHubQueue : public pdx::Client { // Size of the metadata that buffers in this queue cary. size_t user_metadata_size_{0}; + // Buffers and related data that are available for dequeue. + std::priority_queue<Entry, std::vector<Entry>, EntryComparator> + available_buffers_; + + // Slot of the buffers that are not available for normal dequeue. For example, + // the slot of posted or acquired buffers in the perspective of a producer. + std::vector<size_t> unavailable_buffers_slot_; + private: void Initialize(); @@ -250,11 +259,7 @@ class BufferHubQueue : public pdx::Client { // Tracks the buffers belonging to this queue. Buffers are stored according to // "slot" in this vector. Each slot is a logical id of the buffer within this // queue regardless of its queue position or presence in the ring buffer. - std::array<std::shared_ptr<BufferHubBuffer>, kMaxQueueCapacity> buffers_; - - // Buffers and related data that are available for dequeue. - std::priority_queue<Entry, std::vector<Entry>, EntryComparator> - available_buffers_; + std::array<std::shared_ptr<BufferHubBase>, kMaxQueueCapacity> buffers_; // Keeps track with how many buffers have been added into the queue. size_t capacity_{0}; @@ -331,6 +336,13 @@ class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { pdx::Status<void> AddBuffer(const std::shared_ptr<BufferProducer>& buffer, size_t slot); + // Inserts a ProducerBuffer into the queue. On success, the method returns the + // |slot| number where the new buffer gets inserted. Note that the buffer + // being inserted should be in Gain'ed state prior to the call and it's + // considered as already Dequeued when the function returns. + pdx::Status<size_t> InsertBuffer( + const std::shared_ptr<BufferProducer>& buffer); + // Remove producer buffer from the queue. pdx::Status<void> RemoveBuffer(size_t slot) override; @@ -342,11 +354,30 @@ class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, // and caller should call Post() once it's done writing to release the buffer // to the consumer side. + // @return a buffer in gained state, which was originally in released state. pdx::Status<std::shared_ptr<BufferProducer>> Dequeue( int timeout, size_t* slot, pdx::LocalHandle* release_fence); + + // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, + // and caller should call Post() once it's done writing to release the buffer + // to the consumer side. + // + // @param timeout to dequeue a buffer. + // @param slot is the slot of the output BufferProducer. + // @param release_fence for gaining a buffer. + // @param out_meta metadata of the output buffer. + // @param gain_posted_buffer whether to gain posted buffer if no released + // buffer is available to gain. + // @return a buffer in gained state, which was originally in released state if + // gain_posted_buffer is false, or in posted/released state if + // gain_posted_buffer is true. + // TODO(b/112007999): gain_posted_buffer true is only used to prevent + // libdvrtracking from starving when there are non-responding clients. This + // gain_posted_buffer param can be removed once libdvrtracking start to use + // the new AHardwareBuffer API. pdx::Status<std::shared_ptr<BufferProducer>> Dequeue( int timeout, size_t* slot, DvrNativeBufferMetadata* out_meta, - pdx::LocalHandle* release_fence); + pdx::LocalHandle* release_fence, bool gain_posted_buffer = false); // Enqueues a producer buffer in the queue. pdx::Status<void> Enqueue(const std::shared_ptr<BufferProducer>& buffer, @@ -367,6 +398,16 @@ class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { // arguments as the constructors. explicit ProducerQueue(pdx::LocalChannelHandle handle); ProducerQueue(const ProducerQueueConfig& config, const UsagePolicy& usage); + + // Dequeue a producer buffer to write. The returned buffer in |Gain|'ed mode, + // and caller should call Post() once it's done writing to release the buffer + // to the consumer side. + // + // @param slot the slot of the returned buffer. + // @return a buffer in gained state, which was originally in posted state or + // released state. + pdx::Status<std::shared_ptr<BufferProducer>> DequeueUnacquiredBuffer( + size_t* slot); }; class ConsumerQueue : public BufferHubQueue { diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp index 47a27344bd..159d6dc4b9 100644 --- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp +++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp @@ -1,7 +1,9 @@ #include <base/logging.h> #include <binder/Parcel.h> -#include <private/dvr/buffer_hub_client.h> +#include <dvr/dvr_api.h> #include <private/dvr/buffer_hub_queue_client.h> +#include <private/dvr/consumer_buffer.h> +#include <private/dvr/producer_buffer.h> #include <gtest/gtest.h> #include <poll.h> @@ -111,7 +113,7 @@ TEST_F(BufferHubQueueTest, TestDequeue) { // Consumer acquires a buffer. auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(c1_status.ok()); + EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); auto c1 = c1_status.take(); ASSERT_NE(c1, nullptr); EXPECT_EQ(mi.index, i); @@ -122,6 +124,147 @@ TEST_F(BufferHubQueueTest, TestDequeue) { } } +TEST_F(BufferHubQueueTest, + TestDequeuePostedBufferIfNoAvailableReleasedBuffer_withBufferConsumer) { + ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); + + // Allocate 3 buffers to use. + const size_t test_queue_capacity = 3; + for (int64_t i = 0; i < test_queue_capacity; i++) { + AllocateBuffer(); + } + EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity); + + size_t producer_slot, consumer_slot; + LocalHandle fence; + DvrNativeBufferMetadata mi, mo; + + // Producer posts 2 buffers and remember their posted sequence. + std::deque<size_t> posted_slots; + for (int64_t i = 0; i < 2; i++) { + auto p1_status = + producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); + EXPECT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_NE(p1, nullptr); + + // Producer should not be gaining posted buffer when there are still + // available buffers to gain. + auto found_iter = + std::find(posted_slots.begin(), posted_slots.end(), producer_slot); + EXPECT_EQ(found_iter, posted_slots.end()); + posted_slots.push_back(producer_slot); + + // Producer posts the buffer. + mi.index = i; + EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); + } + + // Consumer acquires one buffer. + auto c1_status = + consumer_queue_->Dequeue(kTimeoutMs, &consumer_slot, &mo, &fence); + EXPECT_TRUE(c1_status.ok()); + auto c1 = c1_status.take(); + ASSERT_NE(c1, nullptr); + // Consumer should get the oldest posted buffer. No checks here. + // posted_slots[0] should be in acquired state now. + EXPECT_EQ(mo.index, 0); + // Consumer releases the buffer. + EXPECT_EQ(c1->ReleaseAsync(&mi, LocalHandle()), 0); + // posted_slots[0] should be in released state now. + + // Producer gain and post 2 buffers. + for (int64_t i = 0; i < 2; i++) { + auto p1_status = + producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); + EXPECT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_NE(p1, nullptr); + + // The gained buffer should be the one in released state or the one haven't + // been use. + EXPECT_NE(posted_slots[1], producer_slot); + + mi.index = i + 2; + EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); + } + + // Producer gains a buffer. + auto p1_status = + producer_queue_->Dequeue(kTimeoutMs, &producer_slot, &mo, &fence, true); + EXPECT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_NE(p1, nullptr); + + // The gained buffer should be the oldest posted buffer. + EXPECT_EQ(posted_slots[1], producer_slot); + + // Producer posts the buffer. + mi.index = 4; + EXPECT_EQ(0, p1->PostAsync(&mi, LocalHandle())); +} + +TEST_F(BufferHubQueueTest, + TestDequeuePostedBufferIfNoAvailableReleasedBuffer_noBufferConsumer) { + ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); + + // Allocate 4 buffers to use. + const size_t test_queue_capacity = 4; + for (int64_t i = 0; i < test_queue_capacity; i++) { + AllocateBuffer(); + } + EXPECT_EQ(producer_queue_->capacity(), test_queue_capacity); + + // Post all allowed buffers and remember their posted sequence. + std::deque<size_t> posted_slots; + for (int64_t i = 0; i < test_queue_capacity; i++) { + size_t slot; + LocalHandle fence; + DvrNativeBufferMetadata mi, mo; + + // Producer gains a buffer. + auto p1_status = + producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true); + EXPECT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_NE(p1, nullptr); + + // Producer should not be gaining posted buffer when there are still + // available buffers to gain. + auto found_iter = std::find(posted_slots.begin(), posted_slots.end(), slot); + EXPECT_EQ(found_iter, posted_slots.end()); + posted_slots.push_back(slot); + + // Producer posts the buffer. + mi.index = i; + EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0); + } + + // Gain posted buffers in sequence. + const int64_t nb_dequeue_all_times = 2; + for (int j = 0; j < nb_dequeue_all_times; ++j) { + for (int i = 0; i < test_queue_capacity; ++i) { + size_t slot; + LocalHandle fence; + DvrNativeBufferMetadata mi, mo; + + // Producer gains a buffer. + auto p1_status = + producer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence, true); + EXPECT_TRUE(p1_status.ok()); + auto p1 = p1_status.take(); + ASSERT_NE(p1, nullptr); + + // The gained buffer should be the oldest posted buffer. + EXPECT_EQ(posted_slots[i], slot); + + // Producer posts the buffer. + mi.index = i + test_queue_capacity * (j + 1); + EXPECT_EQ(p1->PostAsync(&mi, LocalHandle()), 0); + } + } +} + TEST_F(BufferHubQueueTest, TestProducerConsumer) { const size_t kBufferCount = 16; size_t slot; @@ -181,6 +324,42 @@ TEST_F(BufferHubQueueTest, TestProducerConsumer) { } } +TEST_F(BufferHubQueueTest, TestInsertBuffer) { + ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); + + consumer_queue_ = producer_queue_->CreateConsumerQueue(); + ASSERT_TRUE(consumer_queue_ != nullptr); + EXPECT_EQ(producer_queue_->capacity(), 0); + EXPECT_EQ(consumer_queue_->capacity(), 0); + + std::shared_ptr<BufferProducer> p1 = BufferProducer::Create( + kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0); + ASSERT_TRUE(p1 != nullptr); + ASSERT_EQ(p1->GainAsync(), 0); + + // Inserting a posted buffer will fail. + DvrNativeBufferMetadata meta; + EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0); + auto status_or_slot = producer_queue_->InsertBuffer(p1); + EXPECT_FALSE(status_or_slot.ok()); + EXPECT_EQ(status_or_slot.error(), EINVAL); + + // Inserting a gained buffer will succeed. + std::shared_ptr<BufferProducer> p2 = BufferProducer::Create( + kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage); + ASSERT_EQ(p2->GainAsync(), 0); + ASSERT_TRUE(p2 != nullptr); + status_or_slot = producer_queue_->InsertBuffer(p2); + EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage(); + // This is the first buffer inserted, should take slot 0. + size_t slot = status_or_slot.get(); + EXPECT_EQ(slot, 0); + + // Wait and expect the consumer to kick up the newly inserted buffer. + WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs); + EXPECT_EQ(consumer_queue_->capacity(), 1ULL); +} + TEST_F(BufferHubQueueTest, TestRemoveBuffer) { ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); DvrNativeBufferMetadata mo; @@ -211,8 +390,8 @@ TEST_F(BufferHubQueueTest, TestRemoveBuffer) { for (size_t i = 0; i < kBufferCount; i++) { Entry* entry = &buffers[i]; - auto producer_status = producer_queue_->Dequeue( - kTimeoutMs, &entry->slot, &mo, &entry->fence); + auto producer_status = + producer_queue_->Dequeue(kTimeoutMs, &entry->slot, &mo, &entry->fence); ASSERT_TRUE(producer_status.ok()); entry->buffer = producer_status.take(); ASSERT_NE(nullptr, entry->buffer); @@ -409,7 +588,7 @@ TEST_F(BufferHubQueueTest, TestUserMetadata) { mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata); EXPECT_EQ(p1->PostAsync(&mi, {}), 0); auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - EXPECT_TRUE(c1_status.ok()); + EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); auto c1 = c1_status.take(); ASSERT_NE(c1, nullptr); @@ -513,7 +692,7 @@ TEST_F(BufferHubQueueTest, TestAllocateBuffer) { size_t cs1, cs2; auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence); - ASSERT_TRUE(c1_status.ok()); + ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage(); auto c1 = c1_status.take(); ASSERT_NE(c1, nullptr); ASSERT_EQ(consumer_queue_->count(), 0U); @@ -528,6 +707,30 @@ TEST_F(BufferHubQueueTest, TestAllocateBuffer) { ASSERT_EQ(cs2, ps2); } +TEST_F(BufferHubQueueTest, TestAllocateTwoBuffers) { + ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); + ASSERT_EQ(producer_queue_->capacity(), 0); + auto status = producer_queue_->AllocateBuffers( + kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, + kBufferUsage, /*buffer_count=*/2); + ASSERT_TRUE(status.ok()); + std::vector<size_t> buffer_slots = status.take(); + ASSERT_EQ(buffer_slots.size(), 2); + ASSERT_EQ(producer_queue_->capacity(), 2); +} + +TEST_F(BufferHubQueueTest, TestAllocateZeroBuffers) { + ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); + ASSERT_EQ(producer_queue_->capacity(), 0); + auto status = producer_queue_->AllocateBuffers( + kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, + kBufferUsage, /*buffer_count=*/0); + ASSERT_TRUE(status.ok()); + std::vector<size_t> buffer_slots = status.take(); + ASSERT_EQ(buffer_slots.size(), 0); + ASSERT_EQ(producer_queue_->capacity(), 0); +} + TEST_F(BufferHubQueueTest, TestUsageSetMask) { const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; ASSERT_TRUE( @@ -705,7 +908,7 @@ TEST_F(BufferHubQueueTest, TestFreeAllBuffers) { ASSERT_NE(producer_buffer, nullptr); ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0); consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence); - ASSERT_TRUE(consumer_status.ok()); + ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage(); } status = producer_queue_->FreeAllBuffers(); @@ -748,7 +951,7 @@ TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) { Parcel parcel; status_t res; res = output_parcelable.writeToParcel(&parcel); - EXPECT_EQ(res, NO_ERROR); + EXPECT_EQ(res, OK); // After written into parcelable, the output_parcelable is still valid has // keeps the producer channel alive. @@ -770,7 +973,7 @@ TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) { EXPECT_FALSE(input_parcelable.IsValid()); res = input_parcelable.readFromParcel(&parcel); - EXPECT_EQ(res, NO_ERROR); + EXPECT_EQ(res, OK); EXPECT_TRUE(input_parcelable.IsValid()); EXPECT_EQ(producer_queue_, nullptr); @@ -799,7 +1002,7 @@ TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) { // Make sure the buffer can be dequeued from consumer side. auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence); - EXPECT_TRUE(s4.ok()); + EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage(); EXPECT_EQ(consumer_queue_->capacity(), 1U); auto consumer = s4.take(); @@ -840,7 +1043,7 @@ TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) { EXPECT_FALSE(input_parcelable.IsValid()); res = input_parcelable.readFromParcel(&parcel); - EXPECT_EQ(res, NO_ERROR); + EXPECT_EQ(res, OK); EXPECT_TRUE(input_parcelable.IsValid()); consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle()); @@ -866,7 +1069,7 @@ TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) { // Make sure the buffer can be dequeued from consumer side. auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence); - EXPECT_TRUE(s3.ok()); + EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage(); EXPECT_EQ(consumer_queue_->capacity(), 1U); auto consumer = s3.take(); diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp index 4f10f83211..8cc7081e4f 100644 --- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp +++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp @@ -108,8 +108,8 @@ class BufferHubQueueProducerTest : public ::testing::Test { void ConnectProducer() { IGraphicBufferProducer::QueueBufferOutput output; // Can connect the first time. - ASSERT_EQ(NO_ERROR, mProducer->connect(kDummyListener, kTestApi, - kTestControlledByApp, &output)); + ASSERT_EQ(OK, mProducer->connect(kDummyListener, kTestApi, + kTestControlledByApp, &output)); } // Dequeue a buffer in a 'correct' fashion. @@ -170,7 +170,7 @@ TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) { TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); - ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); + ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); } TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) { @@ -186,26 +186,24 @@ TEST_F(BufferHubQueueProducerTest, Query_Succeeds) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int32_t value = -1; - EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value)); + EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value)); EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value)); - EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_HEIGHT, &value)); + EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_HEIGHT, &value)); EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value)); - EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_FORMAT, &value)); + EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_FORMAT, &value)); EXPECT_EQ(kDefaultFormat, value); - EXPECT_EQ(NO_ERROR, - mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value)); + EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value)); EXPECT_LE(0, value); EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value); - EXPECT_EQ(NO_ERROR, + EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value)); EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue - EXPECT_EQ(NO_ERROR, - mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value)); + EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value)); EXPECT_EQ(kDefaultConsumerUsageBits, value); } @@ -243,14 +241,14 @@ TEST_F(BufferHubQueueProducerTest, Queue_Succeeds) { // Request the buffer (pre-requisite for queueing) sp<GraphicBuffer> buffer; - ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); // A generic "valid" input IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; // Queue the buffer back into the BQ - ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); EXPECT_EQ(kDefaultWidth, output.width); EXPECT_EQ(kDefaultHeight, output.height); @@ -313,7 +311,7 @@ TEST_F(BufferHubQueueProducerTest, QueueNoFence_ReturnsError) { ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); sp<GraphicBuffer> buffer; - ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); sp<Fence> nullFence = NULL; @@ -332,7 +330,7 @@ TEST_F(BufferHubQueueProducerTest, QueueTestInvalidScalingMode_ReturnsError) { ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); sp<GraphicBuffer> buffer; - ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder().setScalingMode(-1).build(); @@ -353,7 +351,7 @@ TEST_F(BufferHubQueueProducerTest, QueueCropOutOfBounds_ReturnsError) { ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); sp<GraphicBuffer> buffer; - ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder() @@ -372,7 +370,7 @@ TEST_F(BufferHubQueueProducerTest, CancelBuffer_Succeeds) { ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence)); // Should be able to cancel buffer after a dequeue. - EXPECT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence)); + EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence)); } TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { @@ -380,16 +378,15 @@ TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int minUndequeuedBuffers; - ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &minUndequeuedBuffers)); + ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &minUndequeuedBuffers)); const int minBuffers = 1; const int maxBuffers = BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; - ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false)) - << "async mode: " << false; - ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(minBuffers)) + ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false; + ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(minBuffers)) << "bufferCount: " << minBuffers; // Should now be able to dequeue up to minBuffers times @@ -399,14 +396,14 @@ TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); } - ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers)); + ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers)); // queue the first buffer to enable max dequeued buffer count checking IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; sp<GraphicBuffer> buffer; - ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); - ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); + ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); sp<Fence> fence; for (int i = 0; i < maxBuffers; ++i) { @@ -414,25 +411,24 @@ TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { } // Cancel a buffer, so we can decrease the buffer count - ASSERT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence)); + ASSERT_EQ(OK, mProducer->cancelBuffer(slot, fence)); // Should now be able to decrease the max dequeued count by 1 - ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1)); + ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1)); } TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); int minUndequeuedBuffers; - ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &minUndequeuedBuffers)); + ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &minUndequeuedBuffers)); const int minBuffers = 1; const int maxBuffers = BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers; - ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false)) - << "async mode: " << false; + ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false; // Buffer count was out of range EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0)) << "bufferCount: " << 0; @@ -440,7 +436,7 @@ TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) { << "bufferCount: " << maxBuffers + 1; // Set max dequeue count to 2 - ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(2)); + ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2)); // Dequeue 2 buffers int slot = -1; sp<Fence> fence; @@ -478,7 +474,7 @@ TEST_F(BufferHubQueueProducerTest, ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); // Shouldn't be able to request buffer after disconnect. - ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); + ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer)); } @@ -489,14 +485,14 @@ TEST_F(BufferHubQueueProducerTest, ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); // A generic "valid" input IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); IGraphicBufferProducer::QueueBufferOutput output; // Shouldn't be able to queue buffer after disconnect. - ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); + ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output)); } @@ -507,10 +503,10 @@ TEST_F(BufferHubQueueProducerTest, ASSERT_NO_FATAL_FAILURE(ConnectProducer()); ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); + ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); // Shouldn't be able to cancel buffer after disconnect. - ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); + ASSERT_EQ(OK, mProducer->disconnect(kTestApi)); ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE)); } @@ -524,32 +520,32 @@ TEST_F(BufferHubQueueProducerTest, ConnectDisconnectReconnect) { constexpr int maxDequeuedBuffers = 1; int minUndequeuedBuffers; - EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &minUndequeuedBuffers)); - EXPECT_EQ(NO_ERROR, mProducer->setAsyncMode(false)); - EXPECT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers)); + EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &minUndequeuedBuffers)); + EXPECT_EQ(OK, mProducer->setAsyncMode(false)); + EXPECT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers)); int maxCapacity = maxDequeuedBuffers + minUndequeuedBuffers; // Dequeue, request, and queue all buffers. for (int i = 0; i < maxCapacity; i++) { EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); - EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); + EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); + EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); } // Disconnect then reconnect. - EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); + EXPECT_EQ(OK, mProducer->disconnect(kTestApi)); EXPECT_NO_FATAL_FAILURE(ConnectProducer()); // Dequeue, request, and queue all buffers. for (int i = 0; i < maxCapacity; i++) { EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot)); - EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer)); - EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output)); + EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer)); + EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); } - EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi)); + EXPECT_EQ(OK, mProducer->disconnect(kTestApi)); } TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { @@ -568,21 +564,21 @@ TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { EXPECT_TRUE(dummy_producer_parcelable.IsValid()); // Disconnect producer can be taken out, but only to an invalid parcelable. - ASSERT_EQ(mProducer->disconnect(kTestApi), NO_ERROR); + ASSERT_EQ(mProducer->disconnect(kTestApi), OK); EXPECT_EQ(mProducer->TakeAsParcelable(&dummy_producer_parcelable), BAD_VALUE); EXPECT_FALSE(producer_parcelable.IsValid()); - EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), NO_ERROR); + EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK); EXPECT_TRUE(producer_parcelable.IsValid()); // Should still be able to query buffer dimension after disconnect. int32_t value = -1; - EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value)); + EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value)); EXPECT_EQ(static_cast<uint32_t>(value), kDefaultWidth); - EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), NO_ERROR); + EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), OK); EXPECT_EQ(static_cast<uint32_t>(value), kDefaultHeight); - EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), NO_ERROR); + EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), OK); EXPECT_EQ(value, kDefaultFormat); // But connect to API will fail. @@ -598,7 +594,7 @@ TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { ASSERT_TRUE(new_producer != nullptr); EXPECT_EQ(new_producer->connect(kDummyListener, kTestApi, kTestControlledByApp, &output), - NO_ERROR); + OK); } } // namespace diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp index 9c678815cd..8c354fbc18 100644 --- a/libs/vr/libdisplay/Android.bp +++ b/libs/vr/libdisplay/Android.bp @@ -16,8 +16,8 @@ sourceFiles = [ "display_client.cpp", "display_manager_client.cpp", "display_protocol.cpp", - "vsync_client.cpp", "shared_buffer_helpers.cpp", + "vsync_service.cpp", ] localIncludeFiles = [ diff --git a/libs/vr/libdisplay/display_manager_client.cpp b/libs/vr/libdisplay/display_manager_client.cpp index 974c231375..fdeeb70dfb 100644 --- a/libs/vr/libdisplay/display_manager_client.cpp +++ b/libs/vr/libdisplay/display_manager_client.cpp @@ -1,7 +1,6 @@ #include "include/private/dvr/display_manager_client.h" #include <pdx/default_transport/client_channel_factory.h> -#include <private/dvr/buffer_hub_client.h> #include <private/dvr/buffer_hub_queue_client.h> #include <private/dvr/display_protocol.h> #include <utils/Log.h> diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h index caf3182e91..f8f5b3ddb3 100644 --- a/libs/vr/libdisplay/include/private/dvr/display_client.h +++ b/libs/vr/libdisplay/include/private/dvr/display_client.h @@ -5,7 +5,6 @@ #include <hardware/hwcomposer.h> #include <pdx/client.h> #include <pdx/file_handle.h> -#include <private/dvr/buffer_hub_client.h> #include <private/dvr/buffer_hub_queue_client.h> #include <private/dvr/display_protocol.h> diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_client.h b/libs/vr/libdisplay/include/private/dvr/vsync_client.h deleted file mode 100644 index 1eeb80e09d..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/vsync_client.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef ANDROID_DVR_VSYNC_CLIENT_H_ -#define ANDROID_DVR_VSYNC_CLIENT_H_ - -#include <stdint.h> - -#include <pdx/client.h> - -struct dvr_vsync_client {}; - -namespace android { -namespace dvr { - -/* - * VSyncClient is a remote interface to the vsync service in displayd. - * This class is used to wait for and retrieve information about the - * display vsync. - */ -class VSyncClient : public pdx::ClientBase<VSyncClient>, - public dvr_vsync_client { - public: - /* - * Wait for the next vsync signal. - * The timestamp (in ns) is written into *ts when ts is non-NULL. - */ - int Wait(int64_t* timestamp_ns); - - /* - * Returns the file descriptor used to communicate with the vsync system - * service or -1 on error. - */ - int GetFd(); - - /* - * Clears the select/poll/epoll event so that subsequent calls to - * these will not signal until the next vsync. - */ - int Acknowledge(); - - /* - * Get the timestamp of the last vsync event in ns. This call has - * the same side effect on events as Acknowledge(), which saves - * an IPC message. - */ - int GetLastTimestamp(int64_t* timestamp_ns); - - /* - * Get vsync scheduling info. - * Get the estimated timestamp of the next GPU lens warp preemption event in - * ns. Also returns the corresponding vsync count that the next lens warp - * operation will target. This call has the same side effect on events as - * Acknowledge(), which saves an IPC message. - */ - int GetSchedInfo(int64_t* vsync_period_ns, int64_t* next_timestamp_ns, - uint32_t* next_vsync_count); - - private: - friend BASE; - - VSyncClient(); - explicit VSyncClient(long timeout_ms); - - VSyncClient(const VSyncClient&) = delete; - void operator=(const VSyncClient&) = delete; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_VSYNC_CLIENT_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_service.h b/libs/vr/libdisplay/include/private/dvr/vsync_service.h new file mode 100644 index 0000000000..152464abd1 --- /dev/null +++ b/libs/vr/libdisplay/include/private/dvr/vsync_service.h @@ -0,0 +1,65 @@ +#ifndef ANDROID_DVR_VSYNC_SERVICE_H_ +#define ANDROID_DVR_VSYNC_SERVICE_H_ + +#include <binder/IInterface.h> + +namespace android { +namespace dvr { + +class IVsyncCallback : public IInterface { + public: + DECLARE_META_INTERFACE(VsyncCallback) + + enum { + ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION + }; + + virtual status_t onVsync(int64_t vsync_timestamp) = 0; +}; + +class BnVsyncCallback : public BnInterface<IVsyncCallback> { + public: + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags = 0); +}; + +// Register a callback with IVsyncService to be notified of vsync events and +// timestamps. There's also a shared memory vsync buffer defined in +// dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared +// memory buffer that make it preferable in certain situations: +// +// 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService +// is always available as long as surface flinger is running. +// +// 2. IVsyncService will make a binder callback when a vsync event occurs. This +// allows the client to not write code to implement periodic "get the latest +// vsync" calls, which is necessary with the vsync shared memory buffer. +// +// 3. The IVsyncService provides the real vsync timestamp reported by hardware +// composer, whereas the vsync shared memory buffer only has predicted vsync +// times. +class IVsyncService : public IInterface { +public: + DECLARE_META_INTERFACE(VsyncService) + + static const char* GetServiceName() { return "vrflinger_vsync"; } + + enum { + REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION, + UNREGISTER_CALLBACK + }; + + virtual status_t registerCallback(const sp<IVsyncCallback> callback) = 0; + virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) = 0; +}; + +class BnVsyncService : public BnInterface<IVsyncService> { + public: + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags = 0); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_VSYNC_SERVICE_H_ diff --git a/libs/vr/libdisplay/vsync_client.cpp b/libs/vr/libdisplay/vsync_client.cpp deleted file mode 100644 index bc6cf6cabe..0000000000 --- a/libs/vr/libdisplay/vsync_client.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "include/private/dvr/vsync_client.h" - -#include <log/log.h> - -#include <pdx/default_transport/client_channel_factory.h> -#include <private/dvr/display_protocol.h> - -using android::dvr::display::VSyncProtocol; -using android::pdx::Transaction; - -namespace android { -namespace dvr { - -VSyncClient::VSyncClient(long timeout_ms) - : BASE(pdx::default_transport::ClientChannelFactory::Create( - VSyncProtocol::kClientPath), - timeout_ms) {} - -VSyncClient::VSyncClient() - : BASE(pdx::default_transport::ClientChannelFactory::Create( - VSyncProtocol::kClientPath)) {} - -int VSyncClient::Wait(int64_t* timestamp_ns) { - auto status = InvokeRemoteMethod<VSyncProtocol::Wait>(); - if (!status) { - ALOGE("VSyncClient::Wait: Failed to wait for vsync: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - - if (timestamp_ns != nullptr) { - *timestamp_ns = status.get(); - } - return 0; -} - -int VSyncClient::GetFd() { return event_fd(); } - -int VSyncClient::GetLastTimestamp(int64_t* timestamp_ns) { - auto status = InvokeRemoteMethod<VSyncProtocol::GetLastTimestamp>(); - if (!status) { - ALOGE("VSyncClient::GetLastTimestamp: Failed to get vsync timestamp: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - *timestamp_ns = status.get(); - return 0; -} - -int VSyncClient::GetSchedInfo(int64_t* vsync_period_ns, int64_t* timestamp_ns, - uint32_t* next_vsync_count) { - if (!vsync_period_ns || !timestamp_ns || !next_vsync_count) - return -EINVAL; - - auto status = InvokeRemoteMethod<VSyncProtocol::GetSchedInfo>(); - if (!status) { - ALOGE("VSyncClient::GetSchedInfo:: Failed to get warp timestamp: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - - *vsync_period_ns = status.get().vsync_period_ns; - *timestamp_ns = status.get().timestamp_ns; - *next_vsync_count = status.get().next_vsync_count; - return 0; -} - -int VSyncClient::Acknowledge() { - auto status = InvokeRemoteMethod<VSyncProtocol::Acknowledge>(); - ALOGE_IF(!status, "VSuncClient::Acknowledge: Failed to ack vsync because: %s", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp new file mode 100644 index 0000000000..4668b9836c --- /dev/null +++ b/libs/vr/libdisplay/vsync_service.cpp @@ -0,0 +1,146 @@ +#include "include/private/dvr/vsync_service.h" + +#include <binder/Parcel.h> +#include <log/log.h> + +namespace android { +namespace dvr { + +status_t BnVsyncCallback::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch (code) { + case ON_VSYNC: { + CHECK_INTERFACE(IVsyncCallback, data, reply); + int64_t vsync_timestamp = 0; + status_t result = data.readInt64(&vsync_timestamp); + if (result != OK) { + ALOGE("onVsync failed to readInt64: %d", result); + return result; + } + onVsync(vsync_timestamp); + return OK; + } + default: { + return BBinder::onTransact(code, data, reply, flags); + } + } +} + +class BpVsyncCallback : public BpInterface<IVsyncCallback> { +public: + explicit BpVsyncCallback(const sp<IBinder>& impl) + : BpInterface<IVsyncCallback>(impl) {} + virtual ~BpVsyncCallback() {} + + virtual status_t onVsync(int64_t vsync_timestamp) { + Parcel data, reply; + status_t result = data.writeInterfaceToken( + IVsyncCallback::getInterfaceDescriptor()); + if (result != OK) { + ALOGE("onVsync failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeInt64(vsync_timestamp); + if (result != OK) { + ALOGE("onVsync failed to writeInt64: %d", result); + return result; + } + result = remote()->transact( + BnVsyncCallback::ON_VSYNC, data, &reply, TF_ONE_WAY); + if (result != OK) { + ALOGE("onVsync failed to transact: %d", result); + return result; + } + return result; + } +}; + +IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback"); + + +status_t BnVsyncService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch (code) { + case REGISTER_CALLBACK: { + CHECK_INTERFACE(IVsyncService, data, reply); + sp<IBinder> callback; + status_t result = data.readStrongBinder(&callback); + if (result != OK) { + ALOGE("registerCallback failed to readStrongBinder: %d", result); + return result; + } + registerCallback(interface_cast<IVsyncCallback>(callback)); + return OK; + } + case UNREGISTER_CALLBACK: { + CHECK_INTERFACE(IVsyncService, data, reply); + sp<IBinder> callback; + status_t result = data.readStrongBinder(&callback); + if (result != OK) { + ALOGE("unregisterCallback failed to readStrongBinder: %d", result); + return result; + } + unregisterCallback(interface_cast<IVsyncCallback>(callback)); + return OK; + } + default: { + return BBinder::onTransact(code, data, reply, flags); + } + } +} + +class BpVsyncService : public BpInterface<IVsyncService> { +public: + explicit BpVsyncService(const sp<IBinder>& impl) + : BpInterface<IVsyncService>(impl) {} + virtual ~BpVsyncService() {} + + virtual status_t registerCallback(const sp<IVsyncCallback> callback) { + Parcel data, reply; + status_t result = data.writeInterfaceToken( + IVsyncService::getInterfaceDescriptor()); + if (result != OK) { + ALOGE("registerCallback failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(IInterface::asBinder(callback)); + if (result != OK) { + ALOGE("registerCallback failed to writeStrongBinder: %d", result); + return result; + } + result = remote()->transact( + BnVsyncService::REGISTER_CALLBACK, data, &reply); + if (result != OK) { + ALOGE("registerCallback failed to transact: %d", result); + return result; + } + return result; + } + + virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) { + Parcel data, reply; + status_t result = data.writeInterfaceToken( + IVsyncService::getInterfaceDescriptor()); + if (result != OK) { + ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(IInterface::asBinder(callback)); + if (result != OK) { + ALOGE("unregisterCallback failed to writeStrongBinder: %d", result); + return result; + } + result = remote()->transact( + BnVsyncService::UNREGISTER_CALLBACK, data, &reply); + if (result != OK) { + ALOGE("unregisterCallback failed to transact: %d", result); + return result; + } + return result; + } +}; + +IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService"); + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp index 16906f57cd..81a9b2d26a 100644 --- a/libs/vr/libdvr/Android.bp +++ b/libs/vr/libdvr/Android.bp @@ -19,7 +19,14 @@ cc_library_headers { vendor_available: true, } +cc_library_headers { + name: "libdvr_private_headers", + export_include_dirs: ["."], + vendor_available: false, +} + cflags = [ + "-DDVR_TRACKING_IMPLEMENTED=0", "-DLOG_TAG=\"libdvr\"", "-DTRACE=0", "-Wall", @@ -36,7 +43,7 @@ srcs = [ "dvr_performance.cpp", "dvr_pose.cpp", "dvr_surface.cpp", - "dvr_vsync.cpp", + "dvr_tracking.cpp", ] static_libs = [ @@ -66,7 +73,7 @@ shared_libs = [ ] cc_library_shared { - name: "libdvr", + name: "libdvr.google", owner: "google", cflags: cflags, header_libs: ["libdvr_headers"], @@ -81,7 +88,7 @@ cc_library_shared { // restricting function access in the shared lib makes it inconvenient to use in // test code. cc_library_static { - name: "libdvr_static", + name: "libdvr_static.google", owner: "google", cflags: cflags, header_libs: ["libdvr_headers"], diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp index d14f040f12..e099f6a699 100644 --- a/libs/vr/libdvr/dvr_api.cpp +++ b/libs/vr/libdvr/dvr_api.cpp @@ -12,6 +12,7 @@ #include <dvr/dvr_display_manager.h> #include <dvr/dvr_performance.h> #include <dvr/dvr_surface.h> +#include <dvr/dvr_tracking.h> #include <dvr/dvr_vsync.h> // Headers not yet moved into libdvr. diff --git a/libs/vr/libdvr/dvr_buffer.cpp b/libs/vr/libdvr/dvr_buffer.cpp index baf1f2f5da..c11706fc7f 100644 --- a/libs/vr/libdvr/dvr_buffer.cpp +++ b/libs/vr/libdvr/dvr_buffer.cpp @@ -2,7 +2,8 @@ #include <android/hardware_buffer.h> #include <dvr/dvr_shared_buffers.h> -#include <private/dvr/buffer_hub_client.h> +#include <private/dvr/consumer_buffer.h> +#include <private/dvr/producer_buffer.h> #include <ui/GraphicBuffer.h> #include "dvr_internal.h" diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp index 571558a5bd..f4c6600469 100644 --- a/libs/vr/libdvr/dvr_buffer_queue.cpp +++ b/libs/vr/libdvr/dvr_buffer_queue.cpp @@ -9,7 +9,7 @@ using namespace android; using android::dvr::BufferConsumer; -using android::dvr::BufferHubBuffer; +using android::dvr::BufferHubBase; using android::dvr::BufferProducer; using android::dvr::ConsumerQueue; using android::dvr::ProducerQueue; @@ -439,7 +439,7 @@ void DvrReadBufferQueue::SetBufferRemovedCallback( consumer_queue_->SetBufferRemovedCallback(nullptr); } else { consumer_queue_->SetBufferRemovedCallback( - [callback, context](const std::shared_ptr<BufferHubBuffer>& buffer) { + [callback, context](const std::shared_ptr<BufferHubBase>& buffer) { // When buffer is removed from the queue, the slot is already invalid. auto read_buffer = std::make_unique<DvrReadBuffer>(); read_buffer->read_buffer = diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp index 852f9a4726..fe91b14679 100644 --- a/libs/vr/libdvr/dvr_display_manager.cpp +++ b/libs/vr/libdvr/dvr_display_manager.cpp @@ -2,8 +2,8 @@ #include <dvr/dvr_buffer.h> #include <pdx/rpc/variant.h> -#include <private/dvr/buffer_hub_client.h> #include <private/dvr/buffer_hub_queue_client.h> +#include <private/dvr/consumer_buffer.h> #include <private/dvr/display_client.h> #include <private/dvr/display_manager_client.h> diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h index de8bb96aec..df8125a414 100644 --- a/libs/vr/libdvr/dvr_internal.h +++ b/libs/vr/libdvr/dvr_internal.h @@ -16,8 +16,11 @@ typedef struct DvrWriteBuffer DvrWriteBuffer; namespace android { namespace dvr { -class BufferProducer; -class BufferConsumer; +// TODO(b/116855254): Remove this typedef once rename is complete in libdvr. +// Note that the dvr::BufferProducer and dvr::BufferConsumer were poorly named, +// they should really be named as ProducerBuffer and ConsumerBuffer. +typedef class ProducerBuffer BufferProducer; +typedef class ConsumerBuffer BufferConsumer; class IonBuffer; DvrBuffer* CreateDvrBufferFromIonBuffer( diff --git a/libs/vr/libdvr/dvr_tracking.cpp b/libs/vr/libdvr/dvr_tracking.cpp new file mode 100644 index 0000000000..73addc9a0c --- /dev/null +++ b/libs/vr/libdvr/dvr_tracking.cpp @@ -0,0 +1,82 @@ +#include "include/dvr/dvr_tracking.h" + +#include <utils/Errors.h> +#include <utils/Log.h> + +#if !DVR_TRACKING_IMPLEMENTED + +extern "C" { + +// This file provides the stub implementation of dvrTrackingXXX APIs. On +// platforms that implement these APIs, set -DDVR_TRACKING_IMPLEMENTED=1 in the +// build file. +int dvrTrackingCameraCreate(DvrTrackingCamera**) { + ALOGE("dvrTrackingCameraCreate is not implemented."); + return -ENOSYS; +} + +void dvrTrackingCameraDestroy(DvrTrackingCamera*) { + ALOGE("dvrTrackingCameraDestroy is not implemented."); +} + +int dvrTrackingCameraStart(DvrTrackingCamera*, DvrWriteBufferQueue*) { + ALOGE("dvrTrackingCameraCreate is not implemented."); + return -ENOSYS; +} + +int dvrTrackingCameraStop(DvrTrackingCamera*) { + ALOGE("dvrTrackingCameraCreate is not implemented."); + return -ENOSYS; +} + +int dvrTrackingFeatureExtractorCreate(DvrTrackingFeatureExtractor**) { + ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); + return -ENOSYS; +} + +void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor*) { + ALOGE("dvrTrackingFeatureExtractorDestroy is not implemented."); +} + +int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor*, + DvrTrackingFeatureCallback, void*) { + ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); + return -ENOSYS; +} + +int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor*) { + ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); + return -ENOSYS; +} + +int dvrTrackingFeatureExtractorProcessBuffer(DvrTrackingFeatureExtractor*, + DvrReadBuffer*, + const DvrTrackingBufferMetadata*, + bool*) { + ALOGE("dvrTrackingFeatureExtractorProcessBuffer is not implemented."); + return -ENOSYS; +} + +int dvrTrackingSensorsCreate(DvrTrackingSensors**, const char*) { + ALOGE("dvrTrackingSensorsCreate is not implemented."); + return -ENOSYS; +} + +void dvrTrackingSensorsDestroy(DvrTrackingSensors*) { + ALOGE("dvrTrackingSensorsDestroy is not implemented."); +} + +int dvrTrackingSensorsStart(DvrTrackingSensors*, DvrTrackingSensorEventCallback, + void*) { + ALOGE("dvrTrackingStart is not implemented."); + return -ENOSYS; +} + +int dvrTrackingSensorsStop(DvrTrackingSensors*) { + ALOGE("dvrTrackingStop is not implemented."); + return -ENOSYS; +} + +} // extern "C" + +#endif // DVR_TRACKING_IMPLEMENTED diff --git a/libs/vr/libdvr/dvr_vsync.cpp b/libs/vr/libdvr/dvr_vsync.cpp deleted file mode 100644 index 099240e53a..0000000000 --- a/libs/vr/libdvr/dvr_vsync.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "include/dvr/dvr_vsync.h" - -#include <utils/Log.h> - -#include <private/dvr/vsync_client.h> - -extern "C" { - -struct DvrVSyncClient { - std::unique_ptr<android::dvr::VSyncClient> client; -}; - -int dvrVSyncClientCreate(DvrVSyncClient** client_out) { - auto client = android::dvr::VSyncClient::Create(); - if (!client) { - ALOGE("dvrVSyncClientCreate: Failed to create vsync client!"); - return -EIO; - } - - *client_out = new DvrVSyncClient{std::move(client)}; - return 0; -} - -void dvrVSyncClientDestroy(DvrVSyncClient* client) { delete client; } - -int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns, - int64_t* next_timestamp_ns, - uint32_t* next_vsync_count) { - return client->client->GetSchedInfo(vsync_period_ns, next_timestamp_ns, - next_vsync_count); -} - -} // extern "C" diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h index 80ffc82920..e383bb2cb3 100644 --- a/libs/vr/libdvr/include/dvr/dvr_api.h +++ b/libs/vr/libdvr/include/dvr/dvr_api.h @@ -10,6 +10,7 @@ #include <dvr/dvr_display_types.h> #include <dvr/dvr_hardware_composer_types.h> #include <dvr/dvr_pose.h> +#include <dvr/dvr_tracking_types.h> #ifdef __cplusplus extern "C" { @@ -50,6 +51,12 @@ typedef int32_t DvrGlobalBufferKey; typedef struct DvrSurfaceAttributeValue DvrSurfaceAttributeValue; typedef struct DvrSurfaceAttribute DvrSurfaceAttribute; +typedef struct DvrReadBuffer DvrReadBuffer; +typedef struct DvrTrackingCamera DvrTrackingCamera; +typedef struct DvrTrackingFeatureExtractor DvrTrackingFeatureExtractor; +typedef struct DvrTrackingSensors DvrTrackingSensors; +typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; + // Note: To avoid breaking others during active development, only modify this // struct by appending elements to the end. // If you do feel we should to re-arrange or remove elements, please make a @@ -367,12 +374,45 @@ typedef DvrHwcRecti (*DvrHwcFrameGetLayerDamagedRegionPtr)(DvrHwcFrame* frame, typedef int (*DvrPerformanceSetSchedulerPolicyPtr)( pid_t task_id, const char* scheduler_policy); +// dvr_tracking.h +typedef int (*DvrTrackingCameraCreatePtr)(DvrTrackingCamera** out_camera); +typedef void (*DvrTrackingCameraDestroyPtr)(DvrTrackingCamera* camera); +typedef int (*DvrTrackingCameraStartPtr)(DvrTrackingCamera* camera, + DvrWriteBufferQueue* write_queue); +typedef int (*DvrTrackingCameraStopPtr)(DvrTrackingCamera* camera); + +typedef int (*DvrTrackingFeatureExtractorCreatePtr)( + DvrTrackingFeatureExtractor** out_extractor); +typedef void (*DvrTrackingFeatureExtractorDestroyPtr)( + DvrTrackingFeatureExtractor* extractor); +typedef void (*DvrTrackingFeatureCallback)(void* context, + const DvrTrackingFeatures* event); +typedef int (*DvrTrackingFeatureExtractorStartPtr)( + DvrTrackingFeatureExtractor* extractor, + DvrTrackingFeatureCallback callback, void* context); +typedef int (*DvrTrackingFeatureExtractorStopPtr)( + DvrTrackingFeatureExtractor* extractor); +typedef int (*DvrTrackingFeatureExtractorProcessBufferPtr)( + DvrTrackingFeatureExtractor* extractor, DvrReadBuffer* buffer, + const DvrTrackingBufferMetadata* metadata, bool* out_skipped); + +typedef void (*DvrTrackingSensorEventCallback)(void* context, + DvrTrackingSensorEvent* event); +typedef int (*DvrTrackingSensorsCreatePtr)(DvrTrackingSensors** out_sensors, + const char* mode); +typedef void (*DvrTrackingSensorsDestroyPtr)(DvrTrackingSensors* sensors); +typedef int (*DvrTrackingSensorsStartPtr)( + DvrTrackingSensors* sensors, DvrTrackingSensorEventCallback callback, + void* context); +typedef int (*DvrTrackingSensorsStopPtr)(DvrTrackingSensors* sensors); + // The buffer metadata that an Android Surface (a.k.a. ANativeWindow) // will populate. A DvrWriteBufferQueue must be created with this metadata iff // ANativeWindow access is needed. Please do not remove, modify, or reorder // existing data members. If new fields need to be added, please take extra care // to make sure that new data field is padded properly the size of the struct // stays same. +// TODO(b/118893702): move the definition to libnativewindow or libui struct ALIGNED_DVR_STRUCT(8) DvrNativeBufferMetadata { #ifdef __cplusplus DvrNativeBufferMetadata() @@ -426,11 +466,11 @@ struct ALIGNED_DVR_STRUCT(8) DvrNativeBufferMetadata { // Only applicable for metadata retrieved from GainAsync. This indicates which // consumer has pending fence that producer should epoll on. - uint64_t release_fence_mask; + uint32_t release_fence_mask; // Reserved bytes for so that the struct is forward compatible and padding to // 104 bytes so the size is a multiple of 8. - int32_t reserved[8]; + int32_t reserved[9]; }; #ifdef __cplusplus diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h index f0d8ec6d24..3006b61b81 100644 --- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h +++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h @@ -85,9 +85,9 @@ DVR_V1_API_ENTRY(ReadBufferQueueSetBufferRemovedCallback); DVR_V1_API_ENTRY(ReadBufferQueueHandleEvents); // V-Sync client -DVR_V1_API_ENTRY(VSyncClientCreate); -DVR_V1_API_ENTRY(VSyncClientDestroy); -DVR_V1_API_ENTRY(VSyncClientGetSchedInfo); +DVR_V1_API_ENTRY_DEPRECATED(VSyncClientCreate); +DVR_V1_API_ENTRY_DEPRECATED(VSyncClientDestroy); +DVR_V1_API_ENTRY_DEPRECATED(VSyncClientGetSchedInfo); // Display surface DVR_V1_API_ENTRY(SurfaceCreate); @@ -181,3 +181,20 @@ DVR_V1_API_ENTRY(ReadBufferQueueReleaseBuffer); DVR_V1_API_ENTRY(PoseClientGetDataReader); DVR_V1_API_ENTRY(PoseClientDataCapture); DVR_V1_API_ENTRY(PoseClientDataReaderDestroy); + +// Tracking +DVR_V1_API_ENTRY(TrackingCameraCreate); +DVR_V1_API_ENTRY(TrackingCameraDestroy); +DVR_V1_API_ENTRY(TrackingCameraStart); +DVR_V1_API_ENTRY(TrackingCameraStop); + +DVR_V1_API_ENTRY(TrackingFeatureExtractorCreate); +DVR_V1_API_ENTRY(TrackingFeatureExtractorDestroy); +DVR_V1_API_ENTRY(TrackingFeatureExtractorStart); +DVR_V1_API_ENTRY(TrackingFeatureExtractorStop); +DVR_V1_API_ENTRY(TrackingFeatureExtractorProcessBuffer); + +DVR_V1_API_ENTRY(TrackingSensorsCreate); +DVR_V1_API_ENTRY(TrackingSensorsDestroy); +DVR_V1_API_ENTRY(TrackingSensorsStart); +DVR_V1_API_ENTRY(TrackingSensorsStop); diff --git a/libs/vr/libdvr/include/dvr/dvr_deleter.h b/libs/vr/libdvr/include/dvr/dvr_deleter.h index 943384f802..fe59d1ffba 100644 --- a/libs/vr/libdvr/include/dvr/dvr_deleter.h +++ b/libs/vr/libdvr/include/dvr/dvr_deleter.h @@ -20,7 +20,6 @@ typedef struct DvrSurfaceState DvrSurfaceState; typedef struct DvrSurface DvrSurface; typedef struct DvrHwcClient DvrHwcClient; typedef struct DvrHwcFrame DvrHwcFrame; -typedef struct DvrVSyncClient DvrVSyncClient; void dvrBufferDestroy(DvrBuffer* buffer); void dvrReadBufferDestroy(DvrReadBuffer* read_buffer); @@ -32,7 +31,6 @@ void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state); void dvrSurfaceDestroy(DvrSurface* surface); void dvrHwcClientDestroy(DvrHwcClient* client); void dvrHwcFrameDestroy(DvrHwcFrame* frame); -void dvrVSyncClientDestroy(DvrVSyncClient* client); __END_DECLS @@ -55,7 +53,6 @@ struct DvrObjectDeleter { void operator()(DvrSurface* p) { dvrSurfaceDestroy(p); } void operator()(DvrHwcClient* p) { dvrHwcClientDestroy(p); } void operator()(DvrHwcFrame* p) { dvrHwcFrameDestroy(p); } - void operator()(DvrVSyncClient* p) { dvrVSyncClientDestroy(p); } }; // Helper to define unique pointers for DVR object types. @@ -73,7 +70,6 @@ using UniqueDvrSurfaceState = MakeUniqueDvrPointer<DvrSurfaceState>; using UniqueDvrSurface = MakeUniqueDvrPointer<DvrSurface>; using UniqueDvrHwcClient = MakeUniqueDvrPointer<DvrHwcClient>; using UniqueDvrHwcFrame = MakeUniqueDvrPointer<DvrHwcFrame>; -using UniqueDvrVSyncClient = MakeUniqueDvrPointer<DvrVSyncClient>; // TODO(eieio): Add an adapter for std::shared_ptr that injects the deleter into // the relevant constructors. diff --git a/libs/vr/libdvr/include/dvr/dvr_tracking.h b/libs/vr/libdvr/include/dvr/dvr_tracking.h new file mode 100644 index 0000000000..5e388f391a --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_tracking.h @@ -0,0 +1,185 @@ +#ifndef ANDROID_DVR_TRACKING_H_ +#define ANDROID_DVR_TRACKING_H_ + +#include <stdint.h> +#include <sys/cdefs.h> + +#include <dvr/dvr_tracking_types.h> + +__BEGIN_DECLS + +typedef struct DvrReadBuffer DvrReadBuffer; +typedef struct DvrTrackingCamera DvrTrackingCamera; +typedef struct DvrTrackingFeatureExtractor DvrTrackingFeatureExtractor; +typedef struct DvrTrackingSensors DvrTrackingSensors; +typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; + +// The callback for DvrTrackingFeatureExtractor that will deliver the feature +// events. This callback is passed to dvrTrackingFeatureExtractorStart. +typedef void (*DvrTrackingFeatureCallback)(void* context, + const DvrTrackingFeatures* event); + +// The callback for DvrTrackingSensors session that will deliver the events. +// This callback is passed to dvrTrackingSensorsStart. +typedef void (*DvrTrackingSensorEventCallback)(void* context, + DvrTrackingSensorEvent* event); + +// Creates a DvrTrackingCamera session. +// +// On creation, the session is not in operating mode. Client code must call +// dvrTrackingCameraStart to bootstrap the underlying camera stack. +// +// There is no plan to expose camera configuration through this API. All camera +// parameters are determined by the system optimized for better tracking +// results. See b/78662281 for detailed deprecation plan of this API and the +// Stage 2 of VR tracking data source refactoring. +// +// @param out_camera The pointer of a DvrTrackingCamera will be filled here if +// the method call succeeds. +// @return Zero on success, or negative error code. +int dvrTrackingCameraCreate(DvrTrackingCamera** out_camera); + +// Destroys a DvrTrackingCamera handle. +// +// @param camera The DvrTrackingCamera of interest. +void dvrTrackingCameraDestroy(DvrTrackingCamera* camera); + +// Starts the DvrTrackingCamera. +// +// On successful return, all DvrReadBufferQueue's associated with the given +// write_queue will start to receive buffers from the camera stack. Note that +// clients of this API should not assume the buffer dimension, format, and/or +// usage of the outcoming buffers, as they are governed by the underlying camera +// logic. Also note that it's the client's responsibility to consume buffers +// from DvrReadBufferQueue on time and return them back to the producer; +// otherwise the camera stack might be blocked. +// +// @param camera The DvrTrackingCamera of interest. +// @param write_queue A DvrWriteBufferQueue that the camera stack can use to +// populate the buffer into. The queue must be empty and the camera stack +// will request buffer allocation with proper buffer dimension, format, and +// usage. Note that the write queue must be created with user_metadata_size +// set to sizeof(DvrTrackingBufferMetadata). On success, the write_queue +// handle will become invalid and the ownership of the queue handle will be +// transferred into the camera; otherwise, the write_queue handle will keep +// untouched and the caller still has the ownership. +// @return Zero on success, or negative error code. +int dvrTrackingCameraStart(DvrTrackingCamera* camera, + DvrWriteBufferQueue* write_queue); + +// Stops the DvrTrackingCamera. +// +// On successful return, the DvrWriteBufferQueue set during +// dvrTrackingCameraStart will stop getting new buffers from the camera stack. +// +// @param camera The DvrTrackingCamera of interest. +// @return Zero on success, or negative error code. +int dvrTrackingCameraStop(DvrTrackingCamera* camera); + +// Creates a DvrTrackingSensors session. +// +// This will initialize but not start device sensors (gyro / accel). Upon +// successfull creation, the clients can call dvrTrackingSensorsStart to start +// receiving sensor events. +// +// @param out_sensors The pointer of a DvrTrackingSensors will be filled here if +// the method call succeeds. +// @param mode The sensor mode. +// mode="ndk": Use the Android NDK. +// mode="direct": Use direct mode sensors (lower latency). +// @return Zero on success, or negative error code. +int dvrTrackingSensorsCreate(DvrTrackingSensors** out_sensors, + const char* mode); + +// Destroys a DvrTrackingSensors session. +// +// @param sensors The DvrTrackingSensors struct to destroy. +void dvrTrackingSensorsDestroy(DvrTrackingSensors* sensors); + +// Starts the tracking sensor session. +// +// This will start the device sensors and start pumping the feature and sensor +// events as they arrive. +// +// @param client A tracking client created by dvrTrackingSensorsCreate. +// @param context A client supplied pointer that will be passed to the callback. +// @param callback A callback that will receive the sensor events on an +// arbitrary thread. +// @return Zero on success, or negative error code. +int dvrTrackingSensorsStart(DvrTrackingSensors* sensors, + DvrTrackingSensorEventCallback callback, + void* context); + +// Stops a DvrTrackingSensors session. +// +// This will stop the device sensors. dvrTrackingSensorsStart can be called to +// restart them again. +// +// @param client A tracking client created by dvrTrackingClientCreate. +// @return Zero on success, or negative error code. +int dvrTrackingSensorsStop(DvrTrackingSensors* sensors); + +// Creates a tracking feature extractor. +// +// This will initialize but not start the feature extraction session. Upon +// successful creation, the client can call dvrTrackingFeatureExtractorStart to +// start receiving features. +// +// @param out_extractor The pointer of a DvrTrackingFeatureExtractor will be +// filled here if the method call succeeds. +int dvrTrackingFeatureExtractorCreate( + DvrTrackingFeatureExtractor** out_extractor); + +// Destroys a tracking feature extractor. +// +// @param extractor The DvrTrackingFeatureExtractor to destroy. +void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor* extractor); + +// Starts the tracking feature extractor. +// +// This will start the extractor and start pumping the output feature events to +// the registered callback. Note that this method will create one or more +// threads to handle feature processing. +// +// @param extractor The DvrTrackingFeatureExtractor to destroy. +int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor* extractor, + DvrTrackingFeatureCallback callback, + void* context); + +// Stops the tracking feature extractor. +// +// This will stop the extractor session and clean up all internal resourcse +// related to this extractor. On succssful return, all internal therad started +// by dvrTrackingFeatureExtractorStart should be stopped. +// +// @param extractor The DvrTrackingFeatureExtractor to destroy. +int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor* extractor); + +// Processes one buffer to extract features from. +// +// The buffer will be sent over to DSP for feature extraction. Once the process +// is done, the processing thread will invoke DvrTrackingFeatureCallback with +// newly extracted features. Note that not all buffers will be processed, as the +// underlying DSP can only process buffers at a certain framerate. If a buffer +// needs to be skipped, out_skipped filed will be set to true. Also note that +// for successfully processed stereo buffer, two callbacks (one for each eye) +// will be fired. +// +// @param extractor The DvrTrackingFeatureExtractor to destroy. +// @param buffer The buffer to extract features from. Note that the buffer must +// be in acquired state for the buffer to be processed. Also note that the +// buffer will be released back to its producer on successful return of the +// method. +// @param metadata The metadata associated with the buffer. Should be populated +// by DvrTrackingCamera session as user defined metadata. +// @param out_skipped On successful return, the field will be set to true iff +// the buffer was skipped; and false iff the buffer was processed. This +// field is optional and nullptr can be passed here to ignore the field. +// @return Zero on success, or negative error code. +int dvrTrackingFeatureExtractorProcessBuffer( + DvrTrackingFeatureExtractor* extractor, DvrReadBuffer* buffer, + const DvrTrackingBufferMetadata* metadata, bool* out_skipped); + +__END_DECLS + +#endif // ANDROID_DVR_TRACKING_H_ diff --git a/libs/vr/libdvr/include/dvr/dvr_tracking_types.h b/libs/vr/libdvr/include/dvr/dvr_tracking_types.h new file mode 100644 index 0000000000..81310d2303 --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_tracking_types.h @@ -0,0 +1,104 @@ +#ifndef ANDROID_DVR_TRACKING_TYPES_H_ +#define ANDROID_DVR_TRACKING_TYPES_H_ + +#include <stdint.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +typedef struct DvrTrackingBufferMetadata { + // Specifies the source of this image. + uint32_t camera_mask; + // Specifies the memory format of this image. + uint32_t format; + /// The width of the image data. + uint32_t width; + /// The height of the image data. + uint32_t height; + /// The number of bytes per scanline of image data. + uint32_t stride; + /// The frame number of this image. + int32_t frame_number; + /// The timestamp of this image in nanoseconds. Taken in the middle of the + /// exposure interval. + int64_t timestamp_ns; + // This is the timestamp for recording when the system using the HAL + // received the callback. It will not be populated by the HAL. + int64_t callback_timestamp_ns; + /// The exposure duration of this image in nanoseconds. + int64_t exposure_duration_ns; +} DvrTrackingBufferMetadata; + +// Represents a set of features extracted from a camera frame. Note that this +// should be in sync with TangoHalCallbacks defined in tango-hal.h. +typedef struct DvrTrackingFeatures { + // Specifies the source of the features. + uint32_t camera_mask; + + // This is unused. + uint32_t unused; + + // The timestamp in nanoseconds from the image that generated the features. + // Taken in the middle of the exposure interval. + int64_t timestamp_ns; + + // This is the timestamp for recording when the system using the HAL + // received the callback. It will not be populated by the HAL. + int64_t callback_timestamp_ns; + + // The frame number from the image that generated the features. + int64_t frame_number; + + // The number of features. + int count; + + // An array of 2D image points for each feature in the current image. + // This is sub-pixel refined extremum location at the fine resolution. + float (*positions)[2]; + + // The id of these measurements. + int32_t* ids; + + // The feature descriptors. + uint64_t (*descriptors)[8]; + + // Laplacian scores for each feature. + float* scores; + + // Is this feature a minimum or maximum in the Laplacian image. + // 0 if the feature is a maximum, 1 if it is a minimum. + int32_t* is_minimum; + + // This corresponds to the sub-pixel index of the laplacian image + // that the extremum was found. + float* scales; + + // Computed orientation of keypoint as part of FREAK extraction, except + // it's represented in radians and measured anti-clockwise. + float* angles; + + // Edge scores for each feature. + float* edge_scores; +} DvrTrackingFeatures; + +// Represents a sensor event. +typedef struct DvrTrackingSensorEvent { + // The sensor type. + int32_t sensor; + + // Event type. + int32_t type; + + // This is the timestamp recorded from the device. Taken in the middle + // of the integration interval and adjusted for any low pass filtering. + int64_t timestamp_ns; + + // The event data. + float x; + float y; + float z; +} DvrTrackingSensorEvent; + +__END_DECLS + +#endif // ANDROID_DVR_TRACKING_TYPES_H_ diff --git a/libs/vr/libdvr/include/dvr/dvr_vsync.h b/libs/vr/libdvr/include/dvr/dvr_vsync.h index 87fdf31b2b..498bb5cc6e 100644 --- a/libs/vr/libdvr/include/dvr/dvr_vsync.h +++ b/libs/vr/libdvr/include/dvr/dvr_vsync.h @@ -6,8 +6,6 @@ __BEGIN_DECLS -typedef struct DvrVSyncClient DvrVSyncClient; - // Represents a vsync sample. The size of this struct is 32 bytes. typedef struct __attribute__((packed, aligned(16))) DvrVsync { // The timestamp for the last vsync in nanoseconds. @@ -29,19 +27,6 @@ typedef struct __attribute__((packed, aligned(16))) DvrVsync { uint8_t padding[8]; } DvrVsync; -// Creates a new client to the system vsync service. -int dvrVSyncClientCreate(DvrVSyncClient** client_out); - -// Destroys the vsync client. -void dvrVSyncClientDestroy(DvrVSyncClient* client); - -// Get the estimated timestamp of the next GPU lens warp preemption event in/ -// ns. Also returns the corresponding vsync count that the next lens warp -// operation will target. -int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns, - int64_t* next_timestamp_ns, - uint32_t* next_vsync_count); - __END_DECLS #endif // ANDROID_DVR_VSYNC_H_ diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp index 1ae75fbe04..357dffe193 100644 --- a/libs/vr/libdvr/tests/Android.bp +++ b/libs/vr/libdvr/tests/Android.bp @@ -12,38 +12,36 @@ // See the License for the specific language governing permissions and // limitations under the License. -shared_libraries = [ - "libbase", - "libbinder", - "libbufferhubqueue", - "libcutils", - "libgui", - "liblog", - "libhardware", - "libui", - "libutils", - "libnativewindow", - "libpdx_default_transport", -] - -static_libraries = [ - "libdvr_static", - "libchrome", - "libdvrcommon", - "libdisplay", - "libbroadcastring", -] - cc_test { srcs: [ "dvr_display_manager-test.cpp", "dvr_named_buffer-test.cpp", + "dvr_tracking-test.cpp", ], header_libs: ["libdvr_headers"], - static_libs: static_libraries, - shared_libs: shared_libraries, + static_libs: [ + "libdvr_static.google", + "libchrome", + "libdvrcommon", + "libdisplay", + "libbroadcastring", + ], + shared_libs: [ + "libbase", + "libbinder", + "libbufferhubqueue", + "libcutils", + "libgui", + "liblog", + "libhardware", + "libui", + "libutils", + "libnativewindow", + "libpdx_default_transport", + ], cflags: [ + "-DDVR_TRACKING_IMPLEMENTED=0", "-DLOG_TAG=\"dvr_api-test\"", "-DTRACE=0", "-Wno-missing-field-initializers", @@ -51,4 +49,59 @@ cc_test { "-g", ], name: "dvr_api-test", + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} + +cc_test { + name: "dvr_buffer_queue-test", + + // Includes the dvr_api.h header. Tests should only include "dvr_api.h", + // and shall only get access to |dvrGetApi|, as other symbols are hidden + // from the library. + include_dirs: ["frameworks/native/libs/vr/libdvr/include"], + + srcs: ["dvr_buffer_queue-test.cpp"], + + shared_libs: [ + "libandroid", + "liblog", + ], + + cflags: [ + "-DTRACE=0", + "-O2", + "-g", + ], + + // DTS Should only link to NDK libraries. + sdk_version: "26", + stl: "c++_static", +} + +cc_test { + name: "dvr_display-test", + + include_dirs: [ + "frameworks/native/libs/vr/libdvr/include", + "frameworks/native/libs/nativewindow/include", + ], + + srcs: ["dvr_display-test.cpp"], + + shared_libs: [ + "libandroid", + "liblog", + ], + + cflags: [ + "-DTRACE=0", + "-O2", + "-g", + ], + + // DTS Should only link to NDK libraries. + sdk_version: "26", + stl: "c++_static", } diff --git a/libs/vr/libdvr/tests/Android.mk b/libs/vr/libdvr/tests/Android.mk deleted file mode 100644 index 0f3840d52c..0000000000 --- a/libs/vr/libdvr/tests/Android.mk +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) - -# TODO(b/73133405): Currently, building cc_test against NDK using Android.bp -# doesn't work well. Migrate to use Android.bp once b/73133405 gets fixed. - -include $(CLEAR_VARS) -LOCAL_MODULE:= dvr_buffer_queue-test - -# Includes the dvr_api.h header. Tests should only include "dvr_api.h", -# and shall only get access to |dvrGetApi|, as other symbols are hidden from the -# library. -LOCAL_C_INCLUDES := \ - frameworks/native/libs/vr/libdvr/include \ - -LOCAL_SANITIZE := thread - -LOCAL_SRC_FILES := dvr_buffer_queue-test.cpp - -LOCAL_SHARED_LIBRARIES := \ - libandroid \ - liblog \ - -LOCAL_CFLAGS := \ - -DTRACE=0 \ - -O2 \ - -g \ - -# DTS Should only link to NDK libraries. -LOCAL_SDK_VERSION := 26 -LOCAL_NDK_STL_VARIANT := c++_static - -include $(BUILD_NATIVE_TEST) - - -include $(CLEAR_VARS) -LOCAL_MODULE:= dvr_display-test - -LOCAL_C_INCLUDES := \ - frameworks/native/libs/vr/libdvr/include \ - frameworks/native/libs/nativewindow/include - -LOCAL_SANITIZE := thread - -LOCAL_SRC_FILES := dvr_display-test.cpp - -LOCAL_SHARED_LIBRARIES := \ - libandroid \ - liblog - -LOCAL_CFLAGS := \ - -DTRACE=0 \ - -O2 \ - -g - -# DTS Should only link to NDK libraries. -LOCAL_SDK_VERSION := 26 -LOCAL_NDK_STL_VARIANT := c++_static - -include $(BUILD_NATIVE_TEST)
\ No newline at end of file diff --git a/libs/vr/libdvr/tests/dvr_api_test.h b/libs/vr/libdvr/tests/dvr_api_test.h index d8359e78a8..5d2ec285eb 100644 --- a/libs/vr/libdvr/tests/dvr_api_test.h +++ b/libs/vr/libdvr/tests/dvr_api_test.h @@ -14,7 +14,7 @@ class DvrApiTest : public ::testing::Test { // workaround for an Android NDK bug. See more detail: // https://github.com/android-ndk/ndk/issues/360 flags |= RTLD_NODELETE; - platform_handle_ = dlopen("libdvr.so", flags); + platform_handle_ = dlopen("libdvr.google.so", flags); ASSERT_NE(nullptr, platform_handle_) << "Dvr shared library missing."; auto dvr_get_api = reinterpret_cast<decltype(&dvrGetApi)>( diff --git a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp index 2d5f0043e3..df060973ec 100644 --- a/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp +++ b/libs/vr/libdvr/tests/dvr_buffer_queue-test.cpp @@ -62,7 +62,7 @@ class DvrBufferQueueTest : public DvrApiTest { buffer_removed_count_); } - DvrWriteBufferQueue* write_queue_{nullptr}; + DvrWriteBufferQueue* write_queue_ = nullptr; int buffer_available_count_{0}; int buffer_removed_count_{0}; }; diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp index c9a5c09caa..ed725777aa 100644 --- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp +++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp @@ -1,5 +1,6 @@ #include <android-base/properties.h> #include <base/logging.h> +#include <cutils/properties.h> #include <gtest/gtest.h> #include <log/log.h> #include <poll.h> @@ -479,6 +480,11 @@ TEST_F(DvrDisplayManagerTest, ExpectInt) { #endif TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) { + // This test doesn't apply to standalone vr devices. + if (property_get_bool("ro.boot.vr", false)) { + return; + } + // Get surface state and verify there are no surfaces. ASSERT_STATUS_OK(manager_->UpdateSurfaceState()); ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount()); @@ -518,6 +524,11 @@ TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) { } TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) { + // This test doesn't apply to standalone vr devices. + if (property_get_bool("ro.boot.vr", false)) { + return; + } + // Get surface state and verify there are no surfaces. ASSERT_STATUS_OK(manager_->UpdateSurfaceState()); ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount()); @@ -757,6 +768,11 @@ TEST_F(DvrDisplayManagerTest, SurfaceAttributeTypes) { } TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) { + // This test doesn't apply to standalone vr devices. + if (property_get_bool("ro.boot.vr", false)) { + return; + } + // Create an application surface. auto surface_status = CreateApplicationSurface(); ASSERT_STATUS_OK(surface_status); @@ -825,6 +841,11 @@ TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) { } TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) { + // This test doesn't apply to standalone vr devices. + if (property_get_bool("ro.boot.vr", false)) { + return; + } + // Create an application surface. auto surface_status = CreateApplicationSurface(); ASSERT_STATUS_OK(surface_status); diff --git a/libs/vr/libdvr/tests/dvr_tracking-test.cpp b/libs/vr/libdvr/tests/dvr_tracking-test.cpp new file mode 100644 index 0000000000..3b6d6e1ec4 --- /dev/null +++ b/libs/vr/libdvr/tests/dvr_tracking-test.cpp @@ -0,0 +1,103 @@ +#include <android/log.h> +#include <gtest/gtest.h> + +#include "dvr_api_test.h" + +namespace { + +class DvrTrackingTest : public DvrApiTest {}; + +#if DVR_TRACKING_IMPLEMENTED + +TEST_F(DvrTrackingTest, Implemented) { + ASSERT_TRUE(api_.TrackingCameraCreate != nullptr); + ASSERT_TRUE(api_.TrackingCameraStart != nullptr); + ASSERT_TRUE(api_.TrackingCameraStop != nullptr); + + ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr); +} + +TEST_F(DvrTrackingTest, CameraCreateFailsForInvalidInput) { + int ret; + ret = api_.TrackingCameraCreate(nullptr); + EXPECT_EQ(ret, -EINVAL); + + DvrTrackingCamera* camera = reinterpret_cast<DvrTrackingCamera*>(42); + ret = api_.TrackingCameraCreate(&camera); + EXPECT_EQ(ret, -EINVAL); +} + +TEST_F(DvrTrackingTest, CameraCreateDestroy) { + DvrTrackingCamera* camera = nullptr; + int ret = api_.TrackingCameraCreate(&camera); + + EXPECT_EQ(ret, 0); + ASSERT_TRUE(camera != nullptr); + + api_.TrackingCameraDestroy(camera); +} + +TEST_F(DvrTrackingTest, FeatureExtractorCreateFailsForInvalidInput) { + int ret; + ret = api_.TrackingFeatureExtractorCreate(nullptr); + EXPECT_EQ(ret, -EINVAL); + + DvrTrackingFeatureExtractor* camera = + reinterpret_cast<DvrTrackingFeatureExtractor*>(42); + ret = api_.TrackingFeatureExtractorCreate(&camera); + EXPECT_EQ(ret, -EINVAL); +} + +TEST_F(DvrTrackingTest, FeatureExtractorCreateDestroy) { + DvrTrackingFeatureExtractor* camera = nullptr; + int ret = api_.TrackingFeatureExtractorCreate(&camera); + + EXPECT_EQ(ret, 0); + ASSERT_TRUE(camera != nullptr); + + api_.TrackingFeatureExtractorDestroy(camera); +} + +#else // !DVR_TRACKING_IMPLEMENTED + +TEST_F(DvrTrackingTest, NotImplemented) { + ASSERT_TRUE(api_.TrackingCameraCreate != nullptr); + ASSERT_TRUE(api_.TrackingCameraDestroy != nullptr); + ASSERT_TRUE(api_.TrackingCameraStart != nullptr); + ASSERT_TRUE(api_.TrackingCameraStop != nullptr); + + EXPECT_EQ(api_.TrackingCameraCreate(nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingCameraStart(nullptr, nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingCameraStop(nullptr), -ENOSYS); + + ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr); + + EXPECT_EQ(api_.TrackingFeatureExtractorCreate(nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingFeatureExtractorStart(nullptr, nullptr, nullptr), + -ENOSYS); + EXPECT_EQ(api_.TrackingFeatureExtractorStop(nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingFeatureExtractorProcessBuffer(nullptr, nullptr, + nullptr, nullptr), + -ENOSYS); + + ASSERT_TRUE(api_.TrackingSensorsCreate != nullptr); + ASSERT_TRUE(api_.TrackingSensorsDestroy != nullptr); + ASSERT_TRUE(api_.TrackingSensorsStart != nullptr); + ASSERT_TRUE(api_.TrackingSensorsStop != nullptr); + + EXPECT_EQ(api_.TrackingSensorsCreate(nullptr, nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingSensorsStart(nullptr, nullptr, nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingSensorsStop(nullptr), -ENOSYS); +} + +#endif // DVR_TRACKING_IMPLEMENTED + +} // namespace diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h index 234b24afe4..d38b174184 100644 --- a/libs/vr/libpdx/private/pdx/service.h +++ b/libs/vr/libpdx/private/pdx/service.h @@ -59,9 +59,18 @@ class Channel : public std::enable_shared_from_this<Channel> { virtual ~Channel() {} /* + * Accessors to the pid of the last active client. + */ + pid_t GetActiveProcessId() const { return client_pid_; } + void SetActiveProcessId(pid_t pid) { client_pid_ = pid; } + + /* * Utility to get a shared_ptr reference from the channel context pointer. */ static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info); + + private: + pid_t client_pid_ = 0; }; /* diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp index 68b8dd7ed7..3769162344 100644 --- a/libs/vr/libpdx/service.cpp +++ b/libs/vr/libpdx/service.cpp @@ -318,13 +318,7 @@ Status<void> Message::Reply(const RemoteHandle& handle) { PDX_TRACE_NAME("Message::ReplyFileHandle"); auto svc = service_.lock(); if (!replied_ && svc) { - Status<void> ret; - - if (handle) - ret = svc->endpoint()->MessageReply(this, handle.Get()); - else - ret = svc->endpoint()->MessageReply(this, handle.Get()); - + Status<void> ret = svc->endpoint()->MessageReply(this, handle.Get()); replied_ = ret.ok(); return ret; } else { diff --git a/libs/vr/libpdx_uds/channel_parcelable.cpp b/libs/vr/libpdx_uds/channel_parcelable.cpp index e7bce27045..515684696b 100644 --- a/libs/vr/libpdx_uds/channel_parcelable.cpp +++ b/libs/vr/libpdx_uds/channel_parcelable.cpp @@ -36,7 +36,7 @@ LocalChannelHandle ChannelParcelable::TakeChannelHandle() { } status_t ChannelParcelable::writeToParcel(Parcel* parcel) const { - status_t res = NO_ERROR; + status_t res = OK; if (!IsValid()) { ALOGE("ChannelParcelable::writeToParcel: Invalid channel parcel."); @@ -44,20 +44,20 @@ status_t ChannelParcelable::writeToParcel(Parcel* parcel) const { } res = parcel->writeUint32(kUdsMagicParcelHeader); - if (res != NO_ERROR) { + if (res != OK) { ALOGE("ChannelParcelable::writeToParcel: Cannot write magic: res=%d.", res); return res; } res = parcel->writeFileDescriptor(data_fd_.Get()); - if (res != NO_ERROR) { + if (res != OK) { ALOGE("ChannelParcelable::writeToParcel: Cannot write data fd: res=%d.", res); return res; } res = parcel->writeFileDescriptor(pollin_event_fd_.Get()); - if (res != NO_ERROR) { + if (res != OK) { ALOGE( "ChannelParcelable::writeToParcel: Cannot write pollin event fd: " "res=%d.", @@ -66,7 +66,7 @@ status_t ChannelParcelable::writeToParcel(Parcel* parcel) const { } res = parcel->writeFileDescriptor(pollhup_event_fd_.Get()); - if (res != NO_ERROR) { + if (res != OK) { ALOGE( "ChannelParcelable::writeToParcel: Cannot write pollhup event fd: " "res=%d.", @@ -79,7 +79,7 @@ status_t ChannelParcelable::writeToParcel(Parcel* parcel) const { status_t ChannelParcelable::readFromParcel(const Parcel* parcel) { uint32_t magic = 0; - status_t res = NO_ERROR; + status_t res = OK; if (IsValid()) { ALOGE( @@ -89,7 +89,7 @@ status_t ChannelParcelable::readFromParcel(const Parcel* parcel) { } res = parcel->readUint32(&magic); - if (res != NO_ERROR) { + if (res != OK) { ALOGE("ChannelParcelable::readFromParcel: Failed to read magic: res=%d.", res); return res; diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp index 32d40e8371..ecbfdba7c4 100644 --- a/libs/vr/libpdx_uds/service_endpoint.cpp +++ b/libs/vr/libpdx_uds/service_endpoint.cpp @@ -521,6 +521,9 @@ Status<void> Endpoint::ReceiveMessageForChannel( info.flags = 0; info.service = service_; info.channel = GetChannelState(channel_id); + if (info.channel != nullptr) { + info.channel->SetActiveProcessId(request.cred.pid); + } info.send_len = request.send_len; info.recv_len = request.max_recv_len; info.fd_count = request.file_descriptors.size(); diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp index 233e0fceaa..4f8bdbf383 100644 --- a/libs/vr/libvrflinger/Android.bp +++ b/libs/vr/libvrflinger/Android.bp @@ -20,7 +20,6 @@ sourceFiles = [ "display_surface.cpp", "hardware_composer.cpp", "vr_flinger.cpp", - "vsync_service.cpp", ] includeFiles = [ "include" ] @@ -40,6 +39,7 @@ sharedLibraries = [ "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", + "android.hardware.graphics.composer@2.3", "libbinder", "libbase", "libbufferhubqueue", @@ -64,6 +64,7 @@ sharedLibraries = [ headerLibraries = [ "android.hardware.graphics.composer@2.1-command-buffer", "android.hardware.graphics.composer@2.2-command-buffer", + "android.hardware.graphics.composer@2.3-command-buffer", "libdvr_headers", "libsurfaceflinger_headers", ] @@ -89,3 +90,7 @@ cc_library_static { header_libs: headerLibraries, name: "libvrflinger", } + +subdirs = [ + "tests", +] diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h index 1a200aabd2..9e35a39bfd 100644 --- a/libs/vr/libvrflinger/acquired_buffer.h +++ b/libs/vr/libvrflinger/acquired_buffer.h @@ -2,7 +2,7 @@ #define ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_ #include <pdx/file_handle.h> -#include <private/dvr/buffer_hub_client.h> +#include <private/dvr/consumer_buffer.h> #include <memory> @@ -43,7 +43,7 @@ class AcquiredBuffer { // Accessors for the underlying BufferConsumer, the acquire fence, and the // use-case specific sequence value from the acquisition (see - // private/dvr/buffer_hub_client.h). + // private/dvr/consumer_buffer.h). std::shared_ptr<BufferConsumer> buffer() const { return buffer_; } int acquire_fence() const { return acquire_fence_.Get(); } diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h index 3090bd1e7a..e0f2eddfea 100644 --- a/libs/vr/libvrflinger/display_service.h +++ b/libs/vr/libvrflinger/display_service.h @@ -4,7 +4,6 @@ #include <dvr/dvr_api.h> #include <pdx/service.h> #include <pdx/status.h> -#include <private/dvr/buffer_hub_client.h> #include <private/dvr/bufferhub_rpc.h> #include <private/dvr/display_protocol.h> @@ -60,11 +59,6 @@ class DisplayService : public pdx::ServiceBase<DisplayService> { void SetDisplayConfigurationUpdateNotifier( DisplayConfigurationUpdateNotifier notifier); - using VSyncCallback = HardwareComposer::VSyncCallback; - void SetVSyncCallback(VSyncCallback callback) { - hardware_composer_.SetVSyncCallback(callback); - } - void GrantDisplayOwnership() { hardware_composer_.Enable(); } void SeizeDisplayOwnership() { hardware_composer_.Disable(); } void OnBootFinished() { hardware_composer_.OnBootFinished(); } diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp index b8d5e2ba97..6d259bd3c9 100644 --- a/libs/vr/libvrflinger/hardware_composer.cpp +++ b/libs/vr/libvrflinger/hardware_composer.cpp @@ -1,5 +1,6 @@ #include "hardware_composer.h" +#include <binder/IServiceManager.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> #include <fcntl.h> @@ -52,6 +53,10 @@ const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns"; const char kUseExternalDisplayProperty[] = "persist.vr.use_external_display"; +// Surface flinger uses "VSYNC-sf" and "VSYNC-app" for its version of these +// events. Name ours similarly. +const char kVsyncTraceEventName[] = "VSYNC-vrflinger"; + // How long to wait after boot finishes before we turn the display off. constexpr int kBootFinishedDisplayOffTimeoutSec = 10; @@ -131,6 +136,7 @@ HardwareComposer::~HardwareComposer(void) { UpdatePostThreadState(PostThreadState::Quit, true); if (post_thread_.joinable()) post_thread_.join(); + composer_callback_->SetVsyncService(nullptr); } bool HardwareComposer::Initialize( @@ -147,6 +153,13 @@ bool HardwareComposer::Initialize( primary_display_ = GetDisplayParams(composer, primary_display_id, true); + vsync_service_ = new VsyncService; + sp<IServiceManager> sm(defaultServiceManager()); + auto result = sm->addService(String16(VsyncService::GetServiceName()), + vsync_service_, false); + LOG_ALWAYS_FATAL_IF(result != android::OK, + "addService(%s) failed", VsyncService::GetServiceName()); + post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); LOG_ALWAYS_FATAL_IF( !post_thread_event_fd_, @@ -223,6 +236,7 @@ void HardwareComposer::CreateComposer() { LOG_ALWAYS_FATAL_IF(!composer_callback_->GotFirstHotplug(), "Registered composer callback but didn't get hotplug for primary" " display"); + composer_callback_->SetVsyncService(vsync_service_); } void HardwareComposer::OnPostThreadResumed() { @@ -242,7 +256,10 @@ void HardwareComposer::OnPostThreadPaused() { // Standalones only create the composer client once and then use SetPowerMode // to control the screen on pause/resume. if (!is_standalone_device_) { - composer_callback_ = nullptr; + if (composer_callback_ != nullptr) { + composer_callback_->SetVsyncService(nullptr); + composer_callback_ = nullptr; + } composer_.reset(nullptr); } else { EnableDisplay(*target_display_, false); @@ -336,7 +353,6 @@ HWC::Error HardwareComposer::Present(hwc2_display_t display) { // According to the documentation, this fence is signaled at the time of // vsync/DMA for physical displays. if (error == HWC::Error::None) { - ATRACE_INT("HardwareComposer: VsyncFence", present_fence); retire_fence_fds_.emplace_back(present_fence); } else { ATRACE_INT("HardwareComposer: PresentResult", error); @@ -775,6 +791,11 @@ void HardwareComposer::PostThread() { std::unique_lock<std::mutex> lock(post_thread_mutex_); ALOGI("HardwareComposer::PostThread: Entering quiescent state."); + if (was_running) { + vsync_trace_parity_ = false; + ATRACE_INT(kVsyncTraceEventName, 0); + } + // Tear down resources. OnPostThreadPaused(); was_running = false; @@ -848,6 +869,9 @@ void HardwareComposer::PostThread() { vsync_timestamp = status.get(); } + vsync_trace_parity_ = !vsync_trace_parity_; + ATRACE_INT(kVsyncTraceEventName, vsync_trace_parity_ ? 1 : 0); + // Advance the vsync counter only if the system is keeping up with hardware // vsync to give clients an indication of the delays. if (vsync_prediction_interval_ == 1) @@ -867,11 +891,6 @@ void HardwareComposer::PostThread() { vsync_ring_->Publish(vsync); } - // Signal all of the vsync clients. Because absolute time is used for the - // wakeup time below, this can take a little time if necessary. - if (vsync_callback_) - vsync_callback_(vsync_timestamp, /*frame_time_estimate*/ 0, vsync_count_); - { // Sleep until shortly before vsync. ATRACE_NAME("sleep"); @@ -1063,8 +1082,45 @@ void HardwareComposer::UpdateLayerConfig() { layers_.size()); } -void HardwareComposer::SetVSyncCallback(VSyncCallback callback) { - vsync_callback_ = callback; +std::vector<sp<IVsyncCallback>>::const_iterator +HardwareComposer::VsyncService::FindCallback( + const sp<IVsyncCallback>& callback) const { + sp<IBinder> binder = IInterface::asBinder(callback); + return std::find_if(callbacks_.cbegin(), callbacks_.cend(), + [&](const sp<IVsyncCallback>& callback) { + return IInterface::asBinder(callback) == binder; + }); +} + +status_t HardwareComposer::VsyncService::registerCallback( + const sp<IVsyncCallback> callback) { + std::lock_guard<std::mutex> autolock(mutex_); + if (FindCallback(callback) == callbacks_.cend()) { + callbacks_.push_back(callback); + } + return OK; +} + +status_t HardwareComposer::VsyncService::unregisterCallback( + const sp<IVsyncCallback> callback) { + std::lock_guard<std::mutex> autolock(mutex_); + auto iter = FindCallback(callback); + if (iter != callbacks_.cend()) { + callbacks_.erase(iter); + } + return OK; +} + +void HardwareComposer::VsyncService::OnVsync(int64_t vsync_timestamp) { + ATRACE_NAME("VsyncService::OnVsync"); + std::lock_guard<std::mutex> autolock(mutex_); + for (auto iter = callbacks_.begin(); iter != callbacks_.end();) { + if ((*iter)->onVsync(vsync_timestamp) == android::DEAD_OBJECT) { + iter = callbacks_.erase(iter); + } else { + ++iter; + } + } } Return<void> HardwareComposer::ComposerCallback::onHotplug( @@ -1123,16 +1179,26 @@ Return<void> HardwareComposer::ComposerCallback::onRefresh( Return<void> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display, int64_t timestamp) { + TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|", + display, timestamp); + std::lock_guard<std::mutex> lock(mutex_); DisplayInfo* display_info = GetDisplayInfo(display); if (display_info) { - TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|", - display, timestamp); display_info->callback_vsync_timestamp = timestamp; } + if (primary_display_.id == display && vsync_service_ != nullptr) { + vsync_service_->OnVsync(timestamp); + } return Void(); } +void HardwareComposer::ComposerCallback::SetVsyncService( + const sp<VsyncService>& vsync_service) { + std::lock_guard<std::mutex> lock(mutex_); + vsync_service_ = vsync_service; +} + HardwareComposer::ComposerCallback::Displays HardwareComposer::ComposerCallback::GetDisplays() { std::lock_guard<std::mutex> lock(mutex_); @@ -1149,6 +1215,7 @@ HardwareComposer::ComposerCallback::GetDisplays() { Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime( hwc2_display_t display) { + std::lock_guard<std::mutex> autolock(mutex_); DisplayInfo* display_info = GetDisplayInfo(display); if (!display_info) { ALOGW("Attempt to get vsync time for unknown display %" PRIu64, display); @@ -1160,7 +1227,6 @@ Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime( if (!event_fd) { // Fall back to returning the last timestamp returned by the vsync // callback. - std::lock_guard<std::mutex> autolock(mutex_); return display_info->callback_vsync_timestamp; } diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h index 539a7fb6d7..6c25b3ee12 100644 --- a/libs/vr/libvrflinger/hardware_composer.h +++ b/libs/vr/libvrflinger/hardware_composer.h @@ -22,8 +22,8 @@ #include <dvr/dvr_vsync.h> #include <pdx/file_handle.h> #include <pdx/rpc/variant.h> -#include <private/dvr/buffer_hub_client.h> #include <private/dvr/shared_buffer_helpers.h> +#include <private/dvr/vsync_service.h> #include "acquired_buffer.h" #include "display_surface.h" @@ -300,8 +300,6 @@ class Layer { // will access the state and whether it needs to be synchronized. class HardwareComposer { public: - // Type for vsync callback. - using VSyncCallback = std::function<void(int64_t, int64_t, uint32_t)>; using RequestDisplayCallback = std::function<void(bool)>; HardwareComposer(); @@ -325,8 +323,6 @@ class HardwareComposer { std::string Dump(); - void SetVSyncCallback(VSyncCallback callback); - const DisplayParams& GetPrimaryDisplayParams() const { return primary_display_; } @@ -350,6 +346,18 @@ class HardwareComposer { // on/off. Returns true on success, false on failure. bool EnableDisplay(const DisplayParams& display, bool enabled); + class VsyncService : public BnVsyncService { + public: + status_t registerCallback(const sp<IVsyncCallback> callback) override; + status_t unregisterCallback(const sp<IVsyncCallback> callback) override; + void OnVsync(int64_t vsync_timestamp); + private: + std::vector<sp<IVsyncCallback>>::const_iterator FindCallback( + const sp<IVsyncCallback>& callback) const; + std::mutex mutex_; + std::vector<sp<IVsyncCallback>> callbacks_; + }; + class ComposerCallback : public Hwc2::IComposerCallback { public: ComposerCallback() = default; @@ -360,6 +368,7 @@ class HardwareComposer { int64_t timestamp) override; bool GotFirstHotplug() { return got_first_hotplug_; } + void SetVsyncService(const sp<VsyncService>& vsync_service); struct Displays { hwc2_display_t primary_display = 0; @@ -385,6 +394,7 @@ class HardwareComposer { DisplayInfo primary_display_; std::optional<DisplayInfo> external_display_; bool external_display_was_hotplugged_ = false; + sp<VsyncService> vsync_service_; }; HWC::Error Validate(hwc2_display_t display); @@ -484,9 +494,6 @@ class HardwareComposer { // vector must be sorted by surface_id in ascending order. std::vector<Layer> layers_; - // Handler to hook vsync events outside of this class. - VSyncCallback vsync_callback_; - // The layer posting thread. This thread wakes up a short time before vsync to // hand buffers to hardware composer. std::thread post_thread_; @@ -534,6 +541,9 @@ class HardwareComposer { DvrConfig post_thread_config_; std::mutex shared_config_mutex_; + bool vsync_trace_parity_ = false; + sp<VsyncService> vsync_service_; + static constexpr int kPostThreadInterrupted = 1; HardwareComposer(const HardwareComposer&) = delete; diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp new file mode 100644 index 0000000000..c884cb3cfe --- /dev/null +++ b/libs/vr/libvrflinger/tests/Android.bp @@ -0,0 +1,39 @@ +shared_libs = [ + "android.hardware.configstore-utils", + "android.hardware.configstore@1.0", + "libbinder", + "libbufferhubqueue", + "libcutils", + "libgui", + "libhidlbase", + "liblog", + "libui", + "libutils", + "libnativewindow", + "libpdx_default_transport", +] + +static_libs = [ + "libdisplay", +] + +cc_test { + srcs: ["vrflinger_test.cpp"], + // See go/apct-presubmit for documentation on how this .filter file is used + // by Android's automated testing infrastructure for test filtering. + data: ["vrflinger_test.filter"], + static_libs: static_libs, + shared_libs: shared_libs, + cflags: [ + "-DLOG_TAG=\"VrFlingerTest\"", + "-DTRACE=0", + "-O0", + "-g", + "-Wall", + "-Werror", + ], + name: "vrflinger_test", + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp new file mode 100644 index 0000000000..0eb7fec3c4 --- /dev/null +++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp @@ -0,0 +1,261 @@ +#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <android/hardware/configstore/1.1/types.h> +#include <android/hardware_buffer.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/ProcessState.h> +#include <configstore/Utils.h> +#include <cutils/properties.h> +#include <gtest/gtest.h> +#include <gui/ISurfaceComposer.h> +#include <log/log.h> +#include <utils/StrongPointer.h> + +#include <chrono> +#include <memory> +#include <mutex> +#include <optional> +#include <thread> + +#include <private/dvr/display_client.h> + +using namespace android::hardware::configstore; +using namespace android::hardware::configstore::V1_0; +using android::dvr::display::DisplayClient; +using android::dvr::display::Surface; +using android::dvr::display::SurfaceAttribute; +using android::dvr::display::SurfaceAttributeValue; + +namespace android { +namespace dvr { + +// The transaction code for asking surface flinger if vr flinger is active. This +// is done as a hidden api since it's only used for tests. See the "case 1028" +// block in SurfaceFlinger::onTransact() in SurfaceFlinger.cpp. +constexpr uint32_t kIsVrFlingerActiveTransactionCode = 1028; + +// The maximum amount of time to give vr flinger to activate/deactivate. If the +// switch hasn't completed in this amount of time, the test will fail. +constexpr auto kVrFlingerSwitchMaxTime = std::chrono::seconds(1); + +// How long to wait between each check to see if the vr flinger switch +// completed. +constexpr auto kVrFlingerSwitchPollInterval = std::chrono::milliseconds(50); + +// How long to wait for a device that boots to VR to have vr flinger ready. +constexpr auto kBootVrFlingerWaitTimeout = std::chrono::seconds(30); + +// A Binder connection to surface flinger. +class SurfaceFlingerConnection { + public: + static std::unique_ptr<SurfaceFlingerConnection> Create() { + sp<ISurfaceComposer> surface_flinger = interface_cast<ISurfaceComposer>( + defaultServiceManager()->getService(String16("SurfaceFlinger"))); + if (surface_flinger == nullptr) { + return nullptr; + } + + return std::unique_ptr<SurfaceFlingerConnection>( + new SurfaceFlingerConnection(surface_flinger)); + } + + // Returns true if the surface flinger process is still running. We use this + // to detect if surface flinger has crashed. + bool IsAlive() { + IInterface::asBinder(surface_flinger_)->pingBinder(); + return IInterface::asBinder(surface_flinger_)->isBinderAlive(); + } + + // Return true if vr flinger is currently active, false otherwise. If there's + // an error communicating with surface flinger, std::nullopt is returned. + std::optional<bool> IsVrFlingerActive() { + Parcel data, reply; + status_t result = + data.writeInterfaceToken(surface_flinger_->getInterfaceDescriptor()); + if (result != OK) { + return std::nullopt; + } + result = IInterface::asBinder(surface_flinger_) + ->transact(kIsVrFlingerActiveTransactionCode, data, &reply); + if (result != OK) { + return std::nullopt; + } + bool vr_flinger_active; + result = reply.readBool(&vr_flinger_active); + if (result != OK) { + return std::nullopt; + } + return vr_flinger_active; + } + + enum class VrFlingerSwitchResult : int8_t { + kSuccess, + kTimedOut, + kCommunicationError, + kSurfaceFlingerDied + }; + + // Wait for vr flinger to become active or inactive. + VrFlingerSwitchResult WaitForVrFlinger(bool wait_active) { + return WaitForVrFlingerTimed(wait_active, kVrFlingerSwitchPollInterval, + kVrFlingerSwitchMaxTime); + } + + // Wait for vr flinger to become active or inactive, specifying custom timeouts. + VrFlingerSwitchResult WaitForVrFlingerTimed(bool wait_active, + std::chrono::milliseconds pollInterval, std::chrono::seconds timeout) { + auto start_time = std::chrono::steady_clock::now(); + while (1) { + std::this_thread::sleep_for(pollInterval); + if (!IsAlive()) { + return VrFlingerSwitchResult::kSurfaceFlingerDied; + } + std::optional<bool> vr_flinger_active = IsVrFlingerActive(); + if (!vr_flinger_active.has_value()) { + return VrFlingerSwitchResult::kCommunicationError; + } + if (vr_flinger_active.value() == wait_active) { + return VrFlingerSwitchResult::kSuccess; + } else if (std::chrono::steady_clock::now() - start_time > timeout) { + return VrFlingerSwitchResult::kTimedOut; + } + } + } + + private: + SurfaceFlingerConnection(sp<ISurfaceComposer> surface_flinger) + : surface_flinger_(surface_flinger) {} + + sp<ISurfaceComposer> surface_flinger_ = nullptr; +}; + +// This test activates vr flinger by creating a vr flinger surface, then +// deactivates vr flinger by destroying the surface. We verify that vr flinger +// is activated and deactivated as expected, and that surface flinger doesn't +// crash. +// +// If the device doesn't support vr flinger (as repoted by ConfigStore), the +// test does nothing. +// +// If the device is a standalone vr device, the test also does nothing, since +// this test verifies the behavior of display handoff from surface flinger to vr +// flinger and back, and standalone devices never hand control of the display +// back to surface flinger. +TEST(VrFlingerTest, ActivateDeactivate) { + android::ProcessState::self()->startThreadPool(); + + // Exit immediately if the device doesn't support vr flinger. This ConfigStore + // check is the same mechanism used by surface flinger to decide if it should + // initialize vr flinger. + bool vr_flinger_enabled = + getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useVrFlinger>( + false); + if (!vr_flinger_enabled) { + return; + } + + // This test doesn't apply to standalone vr devices. + if (property_get_bool("ro.boot.vr", false)) { + return; + } + + auto surface_flinger_connection = SurfaceFlingerConnection::Create(); + ASSERT_NE(surface_flinger_connection, nullptr); + + // Verify we start off with vr flinger disabled. + ASSERT_TRUE(surface_flinger_connection->IsAlive()); + auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive(); + ASSERT_TRUE(vr_flinger_active.has_value()); + ASSERT_FALSE(vr_flinger_active.value()); + + // Create a vr flinger surface, and verify vr flinger becomes active. + // Introduce a scope so that, at the end of the scope, the vr flinger surface + // is destroyed, and vr flinger deactivates. + { + auto display_client = DisplayClient::Create(); + ASSERT_NE(display_client, nullptr); + auto metrics = display_client->GetDisplayMetrics(); + ASSERT_TRUE(metrics.ok()); + + auto surface = Surface::CreateSurface({ + {SurfaceAttribute::Direct, SurfaceAttributeValue(true)}, + {SurfaceAttribute::Visible, SurfaceAttributeValue(true)}, + }); + ASSERT_TRUE(surface.ok()); + ASSERT_TRUE(surface.get() != nullptr); + + auto queue = surface.get()->CreateQueue( + metrics.get().display_width, metrics.get().display_height, + /*layer_count=*/1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM, + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | + AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + /*capacity=*/1, + /*metadata_size=*/0); + ASSERT_TRUE(queue.ok()); + ASSERT_TRUE(queue.get() != nullptr); + + size_t slot; + pdx::LocalHandle release_fence; + auto buffer = queue.get()->Dequeue(/*timeout=*/0, &slot, &release_fence); + ASSERT_TRUE(buffer.ok()); + ASSERT_TRUE(buffer.get() != nullptr); + + ASSERT_EQ(buffer.get()->width(), metrics.get().display_width); + ASSERT_EQ(buffer.get()->height(), metrics.get().display_height); + + void* raw_buf = nullptr; + ASSERT_GE(buffer.get()->Lock(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + /*x=*/0, /*y=*/0, buffer.get()->width(), + buffer.get()->height(), &raw_buf), + 0); + ASSERT_NE(raw_buf, nullptr); + uint32_t* pixels = static_cast<uint32_t*>(raw_buf); + + for (int i = 0; i < buffer.get()->stride() * buffer.get()->height(); ++i) { + pixels[i] = 0x0000ff00; + } + + ASSERT_GE(buffer.get()->Unlock(), 0); + + ASSERT_GE(buffer.get()->Post(/*ready_fence=*/pdx::LocalHandle()), 0); + + ASSERT_EQ( + surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/true), + SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess); + } + + // Now that the vr flinger surface is destroyed, vr flinger should deactivate. + ASSERT_EQ( + surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/false), + SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess); +} + +// This test runs only on devices that boot to vr. Such a device should boot to +// a state where vr flinger is running, and the test verifies this after a +// delay. +TEST(BootVrFlingerTest, BootsToVrFlinger) { + // Exit if we are not running on a device that boots to vr. + if (!property_get_bool("ro.boot.vr", false)) { + return; + } + + auto surface_flinger_connection = SurfaceFlingerConnection::Create(); + ASSERT_NE(surface_flinger_connection, nullptr); + + // Verify that vr flinger is enabled. + ASSERT_TRUE(surface_flinger_connection->IsAlive()); + auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive(); + ASSERT_TRUE(vr_flinger_active.has_value()); + + bool active_value = vr_flinger_active.value(); + if (!active_value) { + // Try again, but delay up to 30 seconds. + ASSERT_EQ(surface_flinger_connection->WaitForVrFlingerTimed(true, + kVrFlingerSwitchPollInterval, kBootVrFlingerWaitTimeout), + SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess); + } +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.filter b/libs/vr/libvrflinger/tests/vrflinger_test.filter new file mode 100644 index 0000000000..030bb7b67c --- /dev/null +++ b/libs/vr/libvrflinger/tests/vrflinger_test.filter @@ -0,0 +1,5 @@ +{ + "presubmit": { + "filter": "BootVrFlingerTest.*" + } +} diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp index 26aed4f25f..b57383ad44 100644 --- a/libs/vr/libvrflinger/vr_flinger.cpp +++ b/libs/vr/libvrflinger/vr_flinger.cpp @@ -23,7 +23,6 @@ #include "DisplayHardware/ComposerHal.h" #include "display_manager_service.h" #include "display_service.h" -#include "vsync_service.h" namespace android { namespace dvr { @@ -85,16 +84,6 @@ bool VrFlinger::Init(Hwc2::Composer* hidl, CHECK_ERROR(!service, error, "Failed to create display manager service."); dispatcher_->AddService(service); - service = android::dvr::VSyncService::Create(); - CHECK_ERROR(!service, error, "Failed to create vsync service."); - dispatcher_->AddService(service); - - display_service_->SetVSyncCallback( - std::bind(&android::dvr::VSyncService::VSyncEvent, - std::static_pointer_cast<android::dvr::VSyncService>(service), - std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3)); - dispatcher_thread_ = std::thread([this]() { prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0); ALOGI("Entering message loop."); diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp deleted file mode 100644 index b8d8b08b5f..0000000000 --- a/libs/vr/libvrflinger/vsync_service.cpp +++ /dev/null @@ -1,212 +0,0 @@ -#include "vsync_service.h" - -#include <hardware/hwcomposer.h> -#include <log/log.h> -#include <poll.h> -#include <sys/prctl.h> -#include <time.h> -#include <utils/Trace.h> - -#include <dvr/dvr_display_types.h> -#include <pdx/default_transport/service_endpoint.h> -#include <private/dvr/clock_ns.h> -#include <private/dvr/display_protocol.h> - -using android::dvr::display::VSyncProtocol; -using android::dvr::display::VSyncSchedInfo; -using android::pdx::Channel; -using android::pdx::Message; -using android::pdx::MessageInfo; -using android::pdx::default_transport::Endpoint; -using android::pdx::rpc::DispatchRemoteMethod; - -namespace android { -namespace dvr { - -VSyncService::VSyncService() - : BASE("VSyncService", Endpoint::Create(VSyncProtocol::kClientPath)), - last_vsync_(0), - current_vsync_(0), - compositor_time_ns_(0), - current_vsync_count_(0) {} - -VSyncService::~VSyncService() {} - -void VSyncService::VSyncEvent(int64_t timestamp_ns, - int64_t compositor_time_ns, - uint32_t vsync_count) { - ATRACE_NAME("VSyncService::VSyncEvent"); - std::lock_guard<std::mutex> autolock(mutex_); - - last_vsync_ = current_vsync_; - current_vsync_ = timestamp_ns; - compositor_time_ns_ = compositor_time_ns; - current_vsync_count_ = vsync_count; - - NotifyWaiters(); - UpdateClients(); -} - -std::shared_ptr<Channel> VSyncService::OnChannelOpen(pdx::Message& message) { - const MessageInfo& info = message.GetInfo(); - - auto client = std::make_shared<VSyncChannel>(*this, info.pid, info.cid); - AddClient(client); - - return client; -} - -void VSyncService::OnChannelClose(pdx::Message& /*message*/, - const std::shared_ptr<Channel>& channel) { - auto client = std::static_pointer_cast<VSyncChannel>(channel); - if (!client) { - ALOGW("WARNING: VSyncChannel was NULL!!!\n"); - return; - } - - RemoveClient(client); -} - -void VSyncService::AddWaiter(pdx::Message& message) { - std::lock_guard<std::mutex> autolock(mutex_); - std::unique_ptr<VSyncWaiter> waiter(new VSyncWaiter(message)); - waiters_.push_back(std::move(waiter)); -} - -void VSyncService::AddClient(const std::shared_ptr<VSyncChannel>& client) { - std::lock_guard<std::mutex> autolock(mutex_); - clients_.push_back(client); -} - -void VSyncService::RemoveClient(const std::shared_ptr<VSyncChannel>& client) { - std::lock_guard<std::mutex> autolock(mutex_); - clients_.remove(client); -} - -// Private. Assumes mutex is held. -void VSyncService::NotifyWaiters() { - ATRACE_NAME("VSyncService::NotifyWaiters"); - auto first = waiters_.begin(); - auto last = waiters_.end(); - - while (first != last) { - (*first)->Notify(current_vsync_); - waiters_.erase(first++); - } -} - -// Private. Assumes mutex is held. -void VSyncService::UpdateClients() { - ATRACE_NAME("VSyncService::UpdateClients"); - auto first = clients_.begin(); - auto last = clients_.end(); - - while (first != last) { - (*first)->Signal(); - first++; - } -} - -pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) { - ATRACE_NAME("VSyncService::HandleMessage"); - switch (message.GetOp()) { - case VSyncProtocol::Wait::Opcode: - AddWaiter(message); - return {}; - - case VSyncProtocol::GetLastTimestamp::Opcode: - DispatchRemoteMethod<VSyncProtocol::GetLastTimestamp>( - *this, &VSyncService::OnGetLastTimestamp, message); - return {}; - - case VSyncProtocol::GetSchedInfo::Opcode: - DispatchRemoteMethod<VSyncProtocol::GetSchedInfo>( - *this, &VSyncService::OnGetSchedInfo, message); - return {}; - - case VSyncProtocol::Acknowledge::Opcode: - DispatchRemoteMethod<VSyncProtocol::Acknowledge>( - *this, &VSyncService::OnAcknowledge, message); - return {}; - - default: - return Service::HandleMessage(message); - } -} - -pdx::Status<int64_t> VSyncService::OnGetLastTimestamp(pdx::Message& message) { - auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel()); - std::lock_guard<std::mutex> autolock(mutex_); - - // Getting the timestamp has the side effect of ACKing. - client->Ack(); - return {current_vsync_}; -} - -pdx::Status<VSyncSchedInfo> VSyncService::OnGetSchedInfo( - pdx::Message& message) { - auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel()); - std::lock_guard<std::mutex> autolock(mutex_); - - // Getting the timestamp has the side effect of ACKing. - client->Ack(); - - uint32_t next_vsync_count = current_vsync_count_ + 1; - int64_t current_time = GetSystemClockNs(); - int64_t vsync_period_ns = 0; - int64_t next_warp; - if (current_vsync_ == 0 || last_vsync_ == 0) { - // Handle startup when current_vsync_ or last_vsync_ are 0. - // Normally should not happen because vsync_service is running before - // applications, but in case it does a sane time prevents applications - // from malfunctioning. - vsync_period_ns = 20000000; - next_warp = current_time; - } else { - // TODO(jbates) When we have an accurate reading of the true vsync - // period, use that instead of this estimated value. - vsync_period_ns = current_vsync_ - last_vsync_; - // Clamp the period, because when there are no surfaces the last_vsync_ - // value will get stale. Note this is temporary and goes away as soon - // as we have an accurate vsync period reported by the system. - vsync_period_ns = std::min(vsync_period_ns, INT64_C(20000000)); - next_warp = current_vsync_ + vsync_period_ns - compositor_time_ns_; - // If the request missed the present window, move up to the next vsync. - if (current_time > next_warp) { - next_warp += vsync_period_ns; - ++next_vsync_count; - } - } - - return {{vsync_period_ns, next_warp, next_vsync_count}}; -} - -pdx::Status<void> VSyncService::OnAcknowledge(pdx::Message& message) { - auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel()); - std::lock_guard<std::mutex> autolock(mutex_); - client->Ack(); - return {}; -} - -void VSyncWaiter::Notify(int64_t timestamp) { - timestamp_ = timestamp; - DispatchRemoteMethod<VSyncProtocol::Wait>(*this, &VSyncWaiter::OnWait, - message_); -} - -pdx::Status<int64_t> VSyncWaiter::OnWait(pdx::Message& /*message*/) { - return {timestamp_}; -} - -void VSyncChannel::Ack() { - ALOGD_IF(TRACE > 1, "VSyncChannel::Ack: pid=%d cid=%d\n", pid_, cid_); - service_.ModifyChannelEvents(cid_, POLLPRI, 0); -} - -void VSyncChannel::Signal() { - ALOGD_IF(TRACE > 1, "VSyncChannel::Signal: pid=%d cid=%d\n", pid_, cid_); - service_.ModifyChannelEvents(cid_, 0, POLLPRI); -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libvrflinger/vsync_service.h b/libs/vr/libvrflinger/vsync_service.h deleted file mode 100644 index 822f02b266..0000000000 --- a/libs/vr/libvrflinger/vsync_service.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_ -#define ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_ - -#include <pdx/service.h> - -#include <list> -#include <memory> -#include <mutex> -#include <thread> - -#include "display_service.h" - -namespace android { -namespace dvr { - -// VSyncWaiter encapsulates a client blocked waiting for the next vsync. -// It is used to enqueue the Message to reply to when the next vsync event -// occurs. -class VSyncWaiter { - public: - explicit VSyncWaiter(pdx::Message& message) : message_(std::move(message)) {} - - void Notify(int64_t timestamp); - - private: - pdx::Status<int64_t> OnWait(pdx::Message& message); - - pdx::Message message_; - int64_t timestamp_ = 0; - - VSyncWaiter(const VSyncWaiter&) = delete; - void operator=(const VSyncWaiter&) = delete; -}; - -// VSyncChannel manages the service-side per-client context for each client -// using the service. -class VSyncChannel : public pdx::Channel { - public: - VSyncChannel(pdx::Service& service, int pid, int cid) - : service_(service), pid_(pid), cid_(cid) {} - - void Ack(); - void Signal(); - - private: - pdx::Service& service_; - pid_t pid_; - int cid_; - - VSyncChannel(const VSyncChannel&) = delete; - void operator=(const VSyncChannel&) = delete; -}; - -// VSyncService implements the displayd vsync service over ServiceFS. -class VSyncService : public pdx::ServiceBase<VSyncService> { - public: - ~VSyncService() override; - - pdx::Status<void> HandleMessage(pdx::Message& message) override; - - std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override; - void OnChannelClose(pdx::Message& message, - const std::shared_ptr<pdx::Channel>& channel) override; - - // Called by the hardware composer HAL, or similar, whenever a vsync event - // occurs on the primary display. |compositor_time_ns| is the number of ns - // before the next vsync when the compositor will preempt the GPU to do EDS - // and lens warp. - void VSyncEvent(int64_t timestamp_ns, int64_t compositor_time_ns, - uint32_t vsync_count); - - private: - friend BASE; - - VSyncService(); - - pdx::Status<int64_t> OnGetLastTimestamp(pdx::Message& message); - pdx::Status<display::VSyncSchedInfo> OnGetSchedInfo(pdx::Message& message); - pdx::Status<void> OnAcknowledge(pdx::Message& message); - - void NotifierThreadFunction(); - - void AddWaiter(pdx::Message& message); - void NotifyWaiters(); - void UpdateClients(); - - void AddClient(const std::shared_ptr<VSyncChannel>& client); - void RemoveClient(const std::shared_ptr<VSyncChannel>& client); - - int64_t last_vsync_; - int64_t current_vsync_; - int64_t compositor_time_ns_; - uint32_t current_vsync_count_; - - std::mutex mutex_; - - std::list<std::unique_ptr<VSyncWaiter>> waiters_; - std::list<std::shared_ptr<VSyncChannel>> clients_; - - VSyncService(const VSyncService&) = delete; - void operator=(VSyncService&) = delete; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_ diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp index 4acc085428..c72f75e69c 100644 --- a/libs/vr/libvrsensor/pose_client.cpp +++ b/libs/vr/libvrsensor/pose_client.cpp @@ -8,8 +8,8 @@ #include <pdx/client.h> #include <pdx/default_transport/client_channel_factory.h> #include <pdx/file_handle.h> -#include <private/dvr/buffer_hub_client.h> #include <private/dvr/buffer_hub_queue_client.h> +#include <private/dvr/consumer_buffer.h> #include <private/dvr/display_client.h> #include <private/dvr/pose-ipc.h> #include <private/dvr/shared_buffer_helpers.h> @@ -228,7 +228,7 @@ class PoseClient : public pdx::ClientBase<PoseClient> { } constexpr size_t size = DvrVsyncPoseBuffer::kSize * sizeof(DvrPoseAsync); void* addr = nullptr; - int ret = buffer->GetBlobReadOnlyPointer(size, &addr); + int ret = buffer->GetBlobReadWritePointer(size, &addr); if (ret < 0 || !addr) { ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr); return -EIO; diff --git a/libs/vr/public.libraries-google.txt b/libs/vr/public.libraries-google.txt new file mode 100644 index 0000000000..8271b9421f --- /dev/null +++ b/libs/vr/public.libraries-google.txt @@ -0,0 +1 @@ +libdvr.google.so
\ No newline at end of file |