Merge "Fix binder memory handling for 64 bit devices"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index cd0fbd4..16abc1e 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -4,6 +4,7 @@
[Builtin Hooks Options]
# Only turn on clang-format check for the following subfolders.
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+ libs/graphicsenv/
libs/gui/
libs/ui/
libs/vr/
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index e897482..19c2830 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -224,6 +224,11 @@
{ "pagecache", "Page cache", 0, {
{ REQ, "events/filemap/enable" },
} },
+ { "memory", "Memory", 0, {
+ { OPT, "events/kmem/rss_stat/enable" },
+ { OPT, "events/kmem/ion_heap_grow/enable" },
+ { OPT, "events/kmem/ion_heap_shrink/enable" },
+ } },
};
struct TracingVendorCategory {
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index e185cbe..9aa1075 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -121,7 +121,6 @@
"kill",
"librank",
"logcat",
- "logcompressor",
"lsmod",
"lsof",
"netstat",
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 2470a1d..86ec8f8 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -77,7 +77,6 @@
static constexpr const char* kCpPath = "/system/bin/cp";
static constexpr const char* kXattrDefault = "user.default";
-static constexpr const char* kPropHasReserved = "vold.has_reserved";
static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
@@ -343,55 +342,6 @@
return 0;
}
-/**
- * Ensure that we have a hard-limit quota to protect against abusive apps;
- * they should never use more than 90% of blocks or 50% of inodes.
- */
-static int prepare_app_quota(const std::unique_ptr<std::string>& uuid ATTRIBUTE_UNUSED,
- const std::string& device, uid_t uid) {
- // Skip when reserved blocks are protecting us against abusive apps
- if (android::base::GetBoolProperty(kPropHasReserved, false)) return 0;
- // Skip when device no quotas present
- if (device.empty()) return 0;
-
- struct dqblk dq;
- if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
- reinterpret_cast<char*>(&dq)) != 0) {
- PLOG(WARNING) << "Failed to find quota for " << uid;
- return -1;
- }
-
-#if APPLY_HARD_QUOTAS
- if ((dq.dqb_bhardlimit == 0) || (dq.dqb_ihardlimit == 0)) {
- auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
- struct statvfs stat;
- if (statvfs(path.c_str(), &stat) != 0) {
- PLOG(WARNING) << "Failed to statvfs " << path;
- return -1;
- }
-
- dq.dqb_valid = QIF_LIMITS;
- dq.dqb_bhardlimit =
- (((static_cast<uint64_t>(stat.f_blocks) * stat.f_frsize) / 10) * 9) / QIF_DQBLKSIZE;
- dq.dqb_ihardlimit = (stat.f_files / 2);
- if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), device.c_str(), uid,
- reinterpret_cast<char*>(&dq)) != 0) {
- PLOG(WARNING) << "Failed to set hard quota for " << uid;
- return -1;
- } else {
- LOG(DEBUG) << "Applied hard quotas for " << uid;
- return 0;
- }
- } else {
- // Hard quota already set; assume it's reasonable
- return 0;
- }
-#else
- // Hard quotas disabled
- return 0;
-#endif
-}
-
static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) {
if (!property_get_bool("dalvik.vm.usejitprofiles", false)) {
return true;
@@ -506,10 +456,6 @@
return error("Failed to restorecon " + path);
}
- if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), uid)) {
- return error("Failed to set hard quota " + path);
- }
-
if (!prepare_app_profile_dir(packageName, appId, userId)) {
return error("Failed to prepare profiles for " + packageName);
}
@@ -805,7 +751,7 @@
PLOG(WARNING) << "Failed to chmod " << p->fts_path;
}
}
- // Intentional fall through to also set GID
+ [[fallthrough]]; // also set GID
case FTS_F:
if (chown(p->fts_path, -1, expected) != 0) {
PLOG(WARNING) << "Failed to chown " << p->fts_path;
@@ -988,13 +934,6 @@
}
}
- // Data under /data/media doesn't have an app, but we still want
- // to limit it to prevent abuse.
- if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid),
- multiuser_get_uid(userId, AID_MEDIA_RW))) {
- return error("Failed to set hard quota for media_rw");
- }
-
return ok();
}
@@ -1442,7 +1381,7 @@
&& !strcmp(p->fts_parent->fts_parent->fts_parent->fts_name, "Android")) {
p->fts_number = 1;
}
- // Fall through to count the directory
+ [[fallthrough]]; // to count the directory
case FTS_DEFAULT:
case FTS_F:
case FTS_SL:
@@ -1857,13 +1796,14 @@
}
}
}
- // Fall through to always count against total
+ [[fallthrough]]; // always count against total
case FTS_D:
// Ignore data belonging to specific apps
p->fts_number = p->fts_parent->fts_number;
if (p->fts_level == 1 && !strcmp(p->fts_name, "Android")) {
p->fts_number = 1;
}
+ [[fallthrough]]; // always count against total
case FTS_DEFAULT:
case FTS_SL:
case FTS_SLNONE:
@@ -2643,21 +2583,6 @@
reinterpret_cast<char*>(&dq)) == 0) {
LOG(DEBUG) << "Found quota mount " << source << " at " << target;
mQuotaReverseMounts[target] = source;
-
- // ext4 only enables DQUOT_USAGE_ENABLED by default, so we
- // need to kick it again to enable DQUOT_LIMITS_ENABLED. We
- // only need hard limits enabled when we're not being protected
- // by reserved blocks.
- if (!android::base::GetBoolProperty(kPropHasReserved, false)) {
- if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1,
- nullptr) != 0 && errno != EBUSY) {
- PLOG(ERROR) << "Failed to enable USRQUOTA on " << source;
- }
- if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1,
- nullptr) != 0 && errno != EBUSY) {
- PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source;
- }
- }
}
}
#endif
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 28c7658..b2e7047 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -322,21 +322,8 @@
return false;
}
const char* isa = parameters_.instruction_set;
-
- // Check whether the file exists where expected.
std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE;
std::string isa_path = dalvik_cache + "/" + isa;
- std::string art_path = isa_path + "/system@framework@boot.art";
- std::string oat_path = isa_path + "/system@framework@boot.oat";
- bool cleared = false;
- if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) {
- // Files exist, assume everything is alright if not forced. Otherwise clean up.
- if (!force) {
- return true;
- }
- ClearDirectory(isa_path);
- cleared = true;
- }
// Reset umask in otapreopt, so that we control the the access for the files we create.
umask(0);
@@ -355,17 +342,34 @@
}
}
- // Prepare to create.
+ // Check whether we have files in /data.
+ // TODO: check that the files are correct wrt/ jars.
+ std::string art_path = isa_path + "/system@framework@boot.art";
+ std::string oat_path = isa_path + "/system@framework@boot.oat";
+ bool cleared = false;
+ if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) {
+ // Files exist, assume everything is alright if not forced. Otherwise clean up.
+ if (!force) {
+ return true;
+ }
+ ClearDirectory(isa_path);
+ cleared = true;
+ }
+
+ // Check whether we have an image in /system.
+ // TODO: check that the files are correct wrt/ jars.
+ std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa);
+ if (access(preopted_boot_art_path.c_str(), F_OK) == 0) {
+ // Note: we ignore |force| here.
+ return true;
+ }
+
+
if (!cleared) {
ClearDirectory(isa_path);
}
- std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa);
- if (access(preopted_boot_art_path.c_str(), F_OK) != 0) {
- // No preopted boot image. Try to compile.
- return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa);
- }
- return true;
+ return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa);
}
static bool CreatePath(const std::string& path) {
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 48f9eb4..6b9cf0d 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -974,7 +974,7 @@
if (chmod(p->fts_path, target_mode) != 0) {
PLOG(WARNING) << "Failed to chmod " << p->fts_path;
}
- // Intentional fall through to also set GID
+ [[fallthrough]]; // to also set GID
case FTS_F:
if (chown(p->fts_path, -1, gid) != 0) {
PLOG(WARNING) << "Failed to chown " << p->fts_path;
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 5829c4f..d05724a 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -36,8 +36,6 @@
#define BYPASS_QUOTA 0
#define BYPASS_SDCARDFS 0
-#define APPLY_HARD_QUOTAS 0
-
namespace android {
namespace installd {
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index d776682..79cd6b5 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -274,6 +274,7 @@
// Note that we ignore the strict_policy and don't propagate it
// further (since we do no outbound RPCs anyway).
strict_policy = bio_get_uint32(msg);
+ bio_get_uint32(msg); // Ignore worksource header.
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 8c3ff74..7f5177b 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -94,7 +94,8 @@
* int fd = ASharedMemory_create("memory", 128);
*
* // By default it has PROT_READ | PROT_WRITE | PROT_EXEC.
- * char *buffer = (char *) mmap(NULL, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ * size_t memSize = ASharedMemory_getSize(fd);
+ * char *buffer = (char *) mmap(NULL, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
*
* strcpy(buffer, "This is an example."); // trivially initialize content
*
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index f052bcb..7a47724 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -109,6 +109,10 @@
"BC_DEAD_BINDER_DONE"
};
+// 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.
+static const int kUnsetWorkSource = -1;
+
static const char* getReturnString(uint32_t cmd)
{
size_t idx = cmd & 0xff;
@@ -383,6 +387,25 @@
return mStrictModePolicy;
}
+uid_t IPCThreadState::setWorkSource(uid_t uid)
+{
+ uid_t returnValue = mWorkSource;
+ mWorkSource = uid;
+ return returnValue;
+}
+
+uid_t IPCThreadState::getWorkSource() const
+{
+ return mWorkSource;
+}
+
+uid_t IPCThreadState::clearWorkSource()
+{
+ uid_t returnValue = mWorkSource;
+ mWorkSource = kUnsetWorkSource;
+ return returnValue;
+}
+
void IPCThreadState::setLastTransactionBinderFlags(int32_t flags)
{
mLastTransactionBinderFlags = flags;
@@ -736,6 +759,7 @@
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
+ mWorkSource(kUnsetWorkSource),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 87c9842..bc1a71c 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -601,6 +601,7 @@
{
writeInt32(IPCThreadState::self()->getStrictModePolicy() |
STRICT_MODE_PENALTY_GATHER);
+ writeInt32(IPCThreadState::self()->getWorkSource());
// currently the interface identification token is just its name as a string
return writeString16(interface);
}
@@ -613,6 +614,7 @@
bool Parcel::enforceInterface(const String16& interface,
IPCThreadState* threadState) const
{
+ // StrictModePolicy.
int32_t strictPolicy = readInt32();
if (threadState == nullptr) {
threadState = IPCThreadState::self();
@@ -627,6 +629,10 @@
} else {
threadState->setStrictModePolicy(strictPolicy);
}
+ // WorkSource.
+ int32_t workSource = readInt32();
+ threadState->setWorkSource(workSource);
+ // Interface descriptor.
const String16 str(readString16());
if (str == interface) {
return true;
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 40b51ad..5888e14 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -47,6 +47,13 @@
void setStrictModePolicy(int32_t policy);
int32_t getStrictModePolicy() const;
+ // See Binder#setThreadWorkSource in Binder.java.
+ uid_t setWorkSource(uid_t uid);
+ // See Binder#getThreadWorkSource in Binder.java.
+ uid_t getWorkSource() const;
+ // See Binder#clearThreadWorkSource in Binder.java.
+ uid_t clearWorkSource();
+
void setLastTransactionBinderFlags(int32_t flags);
int32_t getLastTransactionBinderFlags() const;
@@ -155,6 +162,9 @@
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;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
IPCThreadStateBase *mIPCThreadStateBase;
diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
index 1a9018a..e37c388 100644
--- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
@@ -42,8 +42,8 @@
/**
* analog using std::shared_ptr for internally held refcount
*
- * ref must be called at least one time during the lifetime of this object. The recommended way to construct
- * this object is with SharedRefBase::make.
+ * ref must be called at least one time during the lifetime of this object. The recommended way to
+ * construct this object is with SharedRefBase::make.
*/
class SharedRefBase {
public:
@@ -77,7 +77,7 @@
/**
* Convenience method for making an object directly with a reference.
*/
- template<class T, class... Args>
+ template <class T, class... Args>
static std::shared_ptr<T> make(Args&&... args) {
T* t = new T(std::forward<Args>(args)...);
return t->template ref<T>();
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 0e97b50..d36b3c0 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -51,6 +51,99 @@
void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29);
/**
+ * This is called to allocate an array with a given length. If allocation fails, null should be
+ * returned.
+ */
+typedef void* (*AParcel_arrayReallocator)(void* vectorData, size_t length);
+
+// @START-PRIMITIVE-VECTOR-GETTERS
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef int32_t* (*AParcel_int32ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef uint32_t* (*AParcel_uint32ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef int64_t* (*AParcel_int64ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef uint64_t* (*AParcel_uint64ArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef float* (*AParcel_floatArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef double* (*AParcel_doubleArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef bool (*AParcel_boolArrayGetter)(const void* arrayData, size_t index);
+
+/**
+ * This is called to set an underlying value in an arrayData object at index.
+ */
+typedef void (*AParcel_boolArraySetter)(void* arrayData, size_t index, bool value);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef char16_t* (*AParcel_charArrayGetter)(void* arrayData);
+
+/**
+ * This is called to get the underlying data from an arrayData object.
+ *
+ * This will never be called for an empty array.
+ */
+typedef int8_t* (*AParcel_byteArrayGetter)(void* arrayData);
+
+// @END-PRIMITIVE-VECTOR-GETTERS
+
+/**
+ * This is called to allocate a buffer
+ *
+ * The length here includes the space required to insert a '\0' for a properly formed c-str. If the
+ * buffer returned from this function is retStr, it will be filled by AParcel_readString with the
+ * data from the remote process, and it will be filled such that retStr[length] == '\0'.
+ *
+ * If allocation fails, null should be returned.
+ */
+typedef void* (*AParcel_stringReallocator)(void* stringData, size_t length);
+
+/**
+ * This is called to get the buffer from a stringData object.
+ */
+typedef char* (*AParcel_stringGetter)(void* stringData);
+
+/**
* Writes an AIBinder to the next location in a non-null parcel. Can be null.
*/
binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) __INTRODUCED_IN(29);
@@ -95,22 +188,6 @@
__INTRODUCED_IN(29);
/**
- * This is called to allocate a buffer
- *
- * The length here includes the space required to insert a '\0' for a properly formed c-str. If the
- * buffer returned from this function is retStr, it will be filled by AParcel_readString with the
- * data from the remote process, and it will be filled such that retStr[length] == '\0'.
- *
- * If allocation fails, null should be returned.
- */
-typedef void* (*AParcel_string_reallocator)(void* stringData, size_t length);
-
-/**
- * This is called to get the buffer from a stringData object.
- */
-typedef char* (*AParcel_string_getter)(void* stringData);
-
-/**
* Reads and allocates string value from the next location in a non-null parcel.
*
* Data is passed to the string allocator once the string size is known. This data should be used to
@@ -121,11 +198,11 @@
* If this function returns a success, the buffer returned by allocator when passed stringData will
* contain a null-terminated c-str read from the binder.
*/
-binder_status_t AParcel_readString(const AParcel* parcel, AParcel_string_reallocator reallocator,
- AParcel_string_getter getter, void** stringData)
+binder_status_t AParcel_readString(const AParcel* parcel, AParcel_stringReallocator reallocator,
+ AParcel_stringGetter getter, void** stringData)
__INTRODUCED_IN(29);
-// @START
+// @START-PRIMITIVE-READ-WRITE
/**
* Writes int32_t value to the next location in a non-null parcel.
*/
@@ -216,7 +293,125 @@
*/
binder_status_t AParcel_readByte(const AParcel* parcel, int8_t* value) __INTRODUCED_IN(29);
-// @END
+/**
+ * Writes an array of int32_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of uint32_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of int64_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of uint64_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of float to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of double to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of bool to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData,
+ AParcel_boolArrayGetter getter, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of char16_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Writes an array of int8_t to the next location in a non-null parcel.
+ */
+binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* value, size_t length)
+ __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of int32_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readInt32Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_int32ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of uint32_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readUint32Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_uint32ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of int64_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readInt64Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_int64ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of uint64_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readUint64Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_uint64ArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of float from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readFloatArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_floatArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of double from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_doubleArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of bool from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readBoolArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_boolArraySetter setter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of char16_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readCharArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_charArrayGetter getter) __INTRODUCED_IN(29);
+
+/**
+ * Reads an array of int8_t from the next location in a non-null parcel.
+ */
+binder_status_t AParcel_readByteArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_byteArrayGetter getter) __INTRODUCED_IN(29);
+
+// @END-PRIMITIVE-READ-WRITE
#endif //__ANDROID_API__ >= __ANDROID_API_Q__
__END_DECLS
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
index d3e6cae..bf0c956 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
@@ -31,14 +31,204 @@
#ifdef __cplusplus
#include <string>
+#include <vector>
namespace ndk {
/**
+ * This resizes a std::vector of some underlying type to the given length.
+ */
+template <typename T>
+static inline void* AParcel_stdVectorReallocator(void* vectorData, size_t length) {
+ std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
+ if (length > vec->max_size()) return nullptr;
+
+ vec->resize(length);
+ return vec;
+}
+
+/**
+ * This retrieves the underlying contiguous vector from a corresponding vectorData.
+ */
+template <typename T>
+static inline T* AParcel_stdVectorGetter(void* vectorData) {
+ std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
+ return vec->data();
+}
+
+/**
+ * This retrieves the underlying value in a vector which may not be contiguous at index from a
+ * corresponding vectorData.
+ */
+template <typename T>
+static inline T AParcel_stdVectorGetter(const void* vectorData, size_t index) {
+ const std::vector<T>* vec = static_cast<const std::vector<T>*>(vectorData);
+ return (*vec)[index];
+}
+
+/**
+ * This sets the underlying value in a corresponding vectorData which may not be contiguous at
+ * index.
+ */
+template <typename T>
+static inline void AParcel_stdVectorSetter(void* vectorData, size_t index, T value) {
+ std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
+ (*vec)[index] = value;
+}
+
+// @START
+/**
+ * Writes a vector of int32_t to the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int32_t>& vec) {
+ return AParcel_writeInt32Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of int32_t from the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<int32_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readInt32Array(parcel, &vectorData, &AParcel_stdVectorReallocator<int32_t>,
+ AParcel_stdVectorGetter<int32_t>);
+}
+
+/**
+ * Writes a vector of uint32_t to the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint32_t>& vec) {
+ return AParcel_writeUint32Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of uint32_t from the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<uint32_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readUint32Array(parcel, &vectorData, &AParcel_stdVectorReallocator<uint32_t>,
+ AParcel_stdVectorGetter<uint32_t>);
+}
+
+/**
+ * Writes a vector of int64_t to the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int64_t>& vec) {
+ return AParcel_writeInt64Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of int64_t from the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<int64_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readInt64Array(parcel, &vectorData, &AParcel_stdVectorReallocator<int64_t>,
+ AParcel_stdVectorGetter<int64_t>);
+}
+
+/**
+ * Writes a vector of uint64_t to the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint64_t>& vec) {
+ return AParcel_writeUint64Array(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of uint64_t from the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<uint64_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readUint64Array(parcel, &vectorData, &AParcel_stdVectorReallocator<uint64_t>,
+ AParcel_stdVectorGetter<uint64_t>);
+}
+
+/**
+ * Writes a vector of float to the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<float>& vec) {
+ return AParcel_writeFloatArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of float from the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<float>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readFloatArray(parcel, &vectorData, &AParcel_stdVectorReallocator<float>,
+ AParcel_stdVectorGetter<float>);
+}
+
+/**
+ * Writes a vector of double to the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<double>& vec) {
+ return AParcel_writeDoubleArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of double from the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<double>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readDoubleArray(parcel, &vectorData, &AParcel_stdVectorReallocator<double>,
+ AParcel_stdVectorGetter<double>);
+}
+
+/**
+ * Writes a vector of bool to the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<bool>& vec) {
+ return AParcel_writeBoolArray(parcel, static_cast<const void*>(&vec),
+ AParcel_stdVectorGetter<bool>, vec.size());
+}
+
+/**
+ * Reads a vector of bool from the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<bool>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readBoolArray(parcel, &vectorData, &AParcel_stdVectorReallocator<bool>,
+ AParcel_stdVectorSetter<bool>);
+}
+
+/**
+ * Writes a vector of char16_t to the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<char16_t>& vec) {
+ return AParcel_writeCharArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of char16_t from the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<char16_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readCharArray(parcel, &vectorData, &AParcel_stdVectorReallocator<char16_t>,
+ AParcel_stdVectorGetter<char16_t>);
+}
+
+/**
+ * Writes a vector of int8_t to the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int8_t>& vec) {
+ return AParcel_writeByteArray(parcel, vec.data(), vec.size());
+}
+
+/**
+ * Reads a vector of int8_t from the next location in a non-null parcel.
+ */
+inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<int8_t>* vec) {
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readByteArray(parcel, &vectorData, &AParcel_stdVectorReallocator<int8_t>,
+ AParcel_stdVectorGetter<int8_t>);
+}
+
+// @END
+
+/**
* Takes a std::string and reallocates it to the specified length. For use with AParcel_readString.
* See use below in AParcel_readString.
*/
-static inline void* AParcel_std_string_reallocator(void* stringData, size_t length) {
+static inline void* AParcel_stdStringReallocator(void* stringData, size_t length) {
std::string* str = static_cast<std::string*>(stringData);
str->resize(length - 1);
return stringData;
@@ -47,7 +237,7 @@
/**
* Takes a std::string and returns the inner char*.
*/
-static inline char* AParcel_std_string_getter(void* stringData) {
+static inline char* AParcel_stdStringGetter(void* stringData) {
std::string* str = static_cast<std::string*>(stringData);
return &(*str)[0];
}
@@ -64,10 +254,31 @@
*/
static inline binder_status_t AParcel_readString(const AParcel* parcel, std::string* str) {
void* stringData = static_cast<void*>(str);
- return AParcel_readString(parcel, AParcel_std_string_reallocator, AParcel_std_string_getter,
+ return AParcel_readString(parcel, AParcel_stdStringReallocator, AParcel_stdStringGetter,
&stringData);
}
+template <typename T>
+static inline binder_status_t AParcel_writeVectorSize(AParcel* parcel, const std::vector<T>& vec) {
+ if (vec.size() > INT32_MAX) {
+ return STATUS_BAD_VALUE;
+ }
+
+ return AParcel_writeInt32(parcel, static_cast<int32_t>(vec.size()));
+}
+
+template <typename T>
+static inline binder_status_t AParcel_resizeVector(const AParcel* parcel, std::vector<T>* vec) {
+ int32_t size;
+ binder_status_t err = AParcel_readInt32(parcel, &size);
+
+ if (err != STATUS_OK) return err;
+ if (size < 0) return STATUS_UNEXPECTED_NULL;
+
+ vec->resize(static_cast<size_t>(size));
+ return STATUS_OK;
+}
+
} // namespace ndk
#endif // __cplusplus
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 2a1bff1..f84814f 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -24,30 +24,48 @@
AIBinder_Weak_promote;
AParcel_delete;
AParcel_readBool;
+ AParcel_readBoolArray;
AParcel_readByte;
+ AParcel_readByteArray;
AParcel_readChar;
+ AParcel_readCharArray;
AParcel_readDouble;
+ AParcel_readDoubleArray;
AParcel_readFloat;
+ AParcel_readFloatArray;
AParcel_readInt32;
+ AParcel_readInt32Array;
AParcel_readInt64;
+ AParcel_readInt64Array;
AParcel_readNullableStrongBinder;
AParcel_readStatusHeader;
AParcel_readString;
AParcel_readStrongBinder;
AParcel_readUint32;
+ AParcel_readUint32Array;
AParcel_readUint64;
+ AParcel_readUint64Array;
AParcel_writeBool;
+ AParcel_writeBoolArray;
AParcel_writeByte;
+ AParcel_writeByteArray;
AParcel_writeChar;
+ AParcel_writeCharArray;
AParcel_writeDouble;
+ AParcel_writeDoubleArray;
AParcel_writeFloat;
+ AParcel_writeFloatArray;
AParcel_writeInt32;
+ AParcel_writeInt32Array;
AParcel_writeInt64;
+ AParcel_writeInt64Array;
AParcel_writeStatusHeader;
AParcel_writeString;
AParcel_writeStrongBinder;
AParcel_writeUint32;
+ AParcel_writeUint32Array;
AParcel_writeUint64;
+ AParcel_writeUint64Array;
AStatus_delete;
AStatus_fromExceptionCode;
AStatus_fromExceptionCodeWithMessage;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 3e03e90..29094db 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -31,6 +31,163 @@
using ::android::sp;
using ::android::status_t;
+template <typename T>
+using ContiguousArrayGetter = T* (*)(void* arrayData);
+template <typename T>
+using ArrayGetter = T (*)(const void* arrayData, size_t index);
+template <typename T>
+using ArraySetter = void (*)(void* arrayData, size_t index, T value);
+
+template <typename T>
+binder_status_t WriteArray(AParcel* parcel, const T* array, size_t length) {
+ if (length > std::numeric_limits<int32_t>::max()) return STATUS_BAD_VALUE;
+
+ Parcel* rawParcel = parcel->get();
+
+ status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
+ if (status != STATUS_OK) return PruneStatusT(status);
+
+ int32_t size = 0;
+ if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY;
+
+ void* const data = rawParcel->writeInplace(size);
+ if (data == nullptr) return STATUS_NO_MEMORY;
+
+ memcpy(data, array, size);
+
+ return STATUS_OK;
+}
+
+// Each element in a char16_t array is converted to an int32_t (not packed).
+template <>
+binder_status_t WriteArray<char16_t>(AParcel* parcel, const char16_t* array, size_t length) {
+ if (length > std::numeric_limits<int32_t>::max()) return STATUS_BAD_VALUE;
+
+ Parcel* rawParcel = parcel->get();
+
+ status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
+ if (status != STATUS_OK) return PruneStatusT(status);
+
+ int32_t size = 0;
+ if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY;
+
+ for (int32_t i = 0; i < length; i++) {
+ status = rawParcel->writeChar(array[i]);
+
+ if (status != STATUS_OK) return PruneStatusT(status);
+ }
+
+ return STATUS_OK;
+}
+
+template <typename T>
+binder_status_t ReadArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator, ContiguousArrayGetter<T> getter) {
+ const Parcel* rawParcel = parcel->get();
+
+ int32_t length;
+ status_t status = rawParcel->readInt32(&length);
+
+ if (status != STATUS_OK) return PruneStatusT(status);
+ if (length < 0) return STATUS_UNEXPECTED_NULL;
+
+ *arrayData = reallocator(*arrayData, length);
+ if (*arrayData == nullptr) return STATUS_NO_MEMORY;
+
+ if (length == 0) return STATUS_OK;
+
+ T* array = getter(*arrayData);
+ if (array == nullptr) return STATUS_NO_MEMORY;
+
+ int32_t size = 0;
+ if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY;
+
+ const void* data = rawParcel->readInplace(size);
+ if (data == nullptr) return STATUS_NO_MEMORY;
+
+ memcpy(array, data, size);
+
+ return STATUS_OK;
+}
+
+// Each element in a char16_t array is converted to an int32_t (not packed)
+template <>
+binder_status_t ReadArray<char16_t>(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ ContiguousArrayGetter<char16_t> getter) {
+ const Parcel* rawParcel = parcel->get();
+
+ int32_t length;
+ status_t status = rawParcel->readInt32(&length);
+
+ if (status != STATUS_OK) return PruneStatusT(status);
+ if (length < 0) return STATUS_UNEXPECTED_NULL;
+
+ *arrayData = reallocator(*arrayData, length);
+ if (*arrayData == nullptr) return STATUS_NO_MEMORY;
+
+ if (length == 0) return STATUS_OK;
+
+ char16_t* array = getter(*arrayData);
+ if (array == nullptr) return STATUS_NO_MEMORY;
+
+ int32_t size = 0;
+ if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY;
+
+ for (int32_t i = 0; i < length; i++) {
+ status = rawParcel->readChar(array + i);
+
+ if (status != STATUS_OK) return PruneStatusT(status);
+ }
+
+ return STATUS_OK;
+}
+
+template <typename T>
+binder_status_t WriteArray(AParcel* parcel, const void* arrayData, ArrayGetter<T> getter,
+ size_t length, status_t (Parcel::*write)(T)) {
+ if (length > std::numeric_limits<int32_t>::max()) return STATUS_BAD_VALUE;
+
+ Parcel* rawParcel = parcel->get();
+
+ status_t status = rawParcel->writeInt32(static_cast<int32_t>(length));
+ if (status != STATUS_OK) return PruneStatusT(status);
+
+ for (size_t i = 0; i < length; i++) {
+ status = (rawParcel->*write)(getter(arrayData, i));
+
+ if (status != STATUS_OK) return PruneStatusT(status);
+ }
+
+ return STATUS_OK;
+}
+
+template <typename T>
+binder_status_t ReadArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator, ArraySetter<T> setter,
+ status_t (Parcel::*read)(T*) const) {
+ const Parcel* rawParcel = parcel->get();
+
+ int32_t length;
+ status_t status = rawParcel->readInt32(&length);
+
+ if (status != STATUS_OK) return PruneStatusT(status);
+ if (length < 0) return STATUS_UNEXPECTED_NULL;
+
+ *arrayData = reallocator(*arrayData, length);
+ if (*arrayData == nullptr) return STATUS_NO_MEMORY;
+
+ for (size_t i = 0; i < length; i++) {
+ T readTarget;
+ status = (rawParcel->*read)(&readTarget);
+ if (status != STATUS_OK) return PruneStatusT(status);
+
+ setter(*arrayData, i, readTarget);
+ }
+
+ return STATUS_OK;
+}
+
void AParcel_delete(AParcel* parcel) {
delete parcel;
}
@@ -98,8 +255,8 @@
return STATUS_OK;
}
-binder_status_t AParcel_readString(const AParcel* parcel, AParcel_string_reallocator reallocator,
- AParcel_string_getter getter, void** stringData) {
+binder_status_t AParcel_readString(const AParcel* parcel, AParcel_stringReallocator reallocator,
+ AParcel_stringGetter getter, void** stringData) {
size_t len16;
const char16_t* str16 = parcel->get()->readString16Inplace(&len16);
@@ -122,10 +279,15 @@
}
*stringData = reallocator(*stringData, len8);
+
+ if (*stringData == nullptr) {
+ return STATUS_NO_MEMORY;
+ }
+
char* str8 = getter(*stringData);
if (str8 == nullptr) {
- LOG(WARNING) << __func__ << ": AParcel_string_allocator failed to allocate.";
+ LOG(WARNING) << __func__ << ": AParcel_stringReallocator failed to allocate.";
return STATUS_NO_MEMORY;
}
@@ -227,4 +389,95 @@
return PruneStatusT(status);
}
+binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* value, size_t length) {
+ return WriteArray<int32_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* value, size_t length) {
+ return WriteArray<uint32_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* value, size_t length) {
+ return WriteArray<int64_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* value, size_t length) {
+ return WriteArray<uint64_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* value, size_t length) {
+ return WriteArray<float>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* value, size_t length) {
+ return WriteArray<double>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData,
+ AParcel_boolArrayGetter getter, size_t length) {
+ return WriteArray<bool>(parcel, arrayData, getter, length, &Parcel::writeBool);
+}
+
+binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* value, size_t length) {
+ return WriteArray<char16_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* value, size_t length) {
+ return WriteArray<int8_t>(parcel, value, length);
+}
+
+binder_status_t AParcel_readInt32Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_int32ArrayGetter getter) {
+ return ReadArray<int32_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readUint32Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_uint32ArrayGetter getter) {
+ return ReadArray<uint32_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readInt64Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_int64ArrayGetter getter) {
+ return ReadArray<int64_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readUint64Array(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_uint64ArrayGetter getter) {
+ return ReadArray<uint64_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readFloatArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_floatArrayGetter getter) {
+ return ReadArray<float>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_doubleArrayGetter getter) {
+ return ReadArray<double>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readBoolArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_boolArraySetter setter) {
+ return ReadArray<bool>(parcel, arrayData, reallocator, setter, &Parcel::readBool);
+}
+
+binder_status_t AParcel_readCharArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_charArrayGetter getter) {
+ return ReadArray<char16_t>(parcel, arrayData, reallocator, getter);
+}
+
+binder_status_t AParcel_readByteArray(const AParcel* parcel, void** arrayData,
+ AParcel_arrayReallocator reallocator,
+ AParcel_byteArrayGetter getter) {
+ return ReadArray<int8_t>(parcel, arrayData, reallocator, getter);
+}
+
// @END
diff --git a/libs/binder/ndk/scripts/gen_parcel_helper.py b/libs/binder/ndk/scripts/gen_parcel_helper.py
index bbd3e5d..2cf10d3 100755
--- a/libs/binder/ndk/scripts/gen_parcel_helper.py
+++ b/libs/binder/ndk/scripts/gen_parcel_helper.py
@@ -30,13 +30,15 @@
("Byte", "int8_t"),
]
-def replaceFileTags(path, content):
+non_contiguously_addressable = {"Bool"}
+
+def replaceFileTags(path, content, start_tag, end_tag):
print("Updating", path)
with open(path, "r+") as f:
lines = f.readlines()
- start = lines.index("// @START\n")
- end = lines.index("// @END\n")
+ start = lines.index("// @" + start_tag + "\n")
+ end = lines.index("// @" + end_tag + "\n")
if end <= start or start < 0 or end < 0:
print("Failed to find tags in", path)
@@ -59,8 +61,10 @@
print("Updating auto-generated code")
+ pre_header = ""
header = ""
source = ""
+ cpp_helper = ""
for pretty, cpp in data_types:
header += "/**\n"
@@ -82,8 +86,95 @@
source += " return PruneStatusT(status);\n"
source += "}\n\n"
- replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", header)
- replaceFileTags(ROOT + "parcel.cpp", source)
+ for pretty, cpp in data_types:
+ nca = pretty in non_contiguously_addressable
+
+ arg_type = "const " + cpp + "* value"
+ if nca: arg_type = "const void* arrayData, AParcel_" + pretty.lower() + "ArrayGetter getter"
+ args = "value, length"
+ if nca: args = "arrayData, getter, length, &Parcel::write" + pretty
+
+ header += "/**\n"
+ header += " * Writes an array of " + cpp + " to the next location in a non-null parcel.\n"
+ header += " */\n"
+ header += "binder_status_t AParcel_write" + pretty + "Array(AParcel* parcel, " + arg_type + ", size_t length) __INTRODUCED_IN(29);\n\n"
+ source += "binder_status_t AParcel_write" + pretty + "Array(AParcel* parcel, " + arg_type + ", size_t length) {\n"
+ source += " return WriteArray<" + cpp + ">(parcel, " + args + ");\n";
+ source += "}\n\n"
+
+ for pretty, cpp in data_types:
+ nca = pretty in non_contiguously_addressable
+
+ extra_getter_args = ""
+ if nca: extra_getter_args = ", size_t index"
+ getter_return = cpp + "*"
+ if nca: getter_return = cpp
+ getter_array_data = "void* arrayData"
+ if nca: getter_array_data = "const void* arrayData"
+
+ getter_type = "AParcel_" + pretty.lower() + "ArrayGetter"
+ setter_type = "AParcel_" + pretty.lower() + "ArraySetter"
+
+ pre_header += "/**\n"
+ pre_header += " * This is called to get the underlying data from an arrayData object.\n"
+ pre_header += " *\n"
+ pre_header += " * This will never be called for an empty array.\n"
+ pre_header += " */\n"
+ pre_header += "typedef " + getter_return + " (*" + getter_type + ")(" + getter_array_data + extra_getter_args + ");\n\n"
+
+ if nca:
+ pre_header += "/**\n"
+ pre_header += " * This is called to set an underlying value in an arrayData object at index.\n"
+ pre_header += " */\n"
+ pre_header += "typedef void (*" + setter_type + ")(void* arrayData, size_t index, " + cpp + " value);\n\n"
+
+ read_using = "getter"
+ if nca: read_using = "setter"
+ read_type = getter_type
+ if nca: read_type = setter_type
+
+ arguments = ["const AParcel* parcel"]
+ arguments += ["void** arrayData"]
+ arguments += ["AParcel_arrayReallocator reallocator"]
+ arguments += [read_type + " " + read_using]
+ arguments = ", ".join(arguments)
+
+ header += "/**\n"
+ header += " * Reads an array of " + cpp + " from the next location in a non-null parcel.\n"
+ header += " */\n"
+ header += "binder_status_t AParcel_read" + pretty + "Array(" + arguments + ") __INTRODUCED_IN(29);\n\n"
+ source += "binder_status_t AParcel_read" + pretty + "Array(" + arguments + ") {\n"
+ additional_args = ""
+ if nca: additional_args = ", &Parcel::read" + pretty
+ source += " return ReadArray<" + cpp + ">(parcel, arrayData, reallocator, " + read_using + additional_args + ");\n";
+ source += "}\n\n"
+
+ cpp_helper += "/**\n"
+ cpp_helper += " * Writes a vector of " + cpp + " to the next location in a non-null parcel.\n"
+ cpp_helper += " */\n"
+ cpp_helper += "inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<" + cpp + ">& vec) {\n"
+ write_args = "vec.data()"
+ if nca: write_args = "static_cast<const void*>(&vec), AParcel_stdVectorGetter<" + cpp + ">"
+ cpp_helper += " return AParcel_write" + pretty + "Array(parcel, " + write_args + ", vec.size());\n"
+ cpp_helper += "}\n\n"
+
+ cpp_helper += "/**\n"
+ cpp_helper += " * Reads a vector of " + cpp + " from the next location in a non-null parcel.\n"
+ cpp_helper += " */\n"
+ cpp_helper += "inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<" + cpp + ">* vec) {\n"
+ cpp_helper += " void* vectorData = static_cast<void*>(vec);\n"
+ read_args = []
+ read_args += ["parcel"]
+ read_args += ["&vectorData"]
+ read_args += ["&AParcel_stdVectorReallocator<" + cpp + ">"]
+ read_args += ["AParcel_stdVector" + read_using.capitalize() + "<" + cpp + ">"]
+ cpp_helper += " return AParcel_read" + pretty + "Array(" + ", ".join(read_args) + ");\n"
+ cpp_helper += "}\n\n"
+
+ replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", pre_header, "START-PRIMITIVE-VECTOR-GETTERS", "END-PRIMITIVE-VECTOR-GETTERS")
+ replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", header, "START-PRIMITIVE-READ-WRITE", "END-PRIMITIVE-READ-WRITE")
+ replaceFileTags(ROOT + "parcel.cpp", source, "START", "END")
+ replaceFileTags(ROOT + "include_ndk/android/binder_parcel_utils.h", cpp_helper, "START", "END")
print("Updating DONE.")
diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/main_client.cpp
index 22bf1e5..6945cac 100644
--- a/libs/binder/ndk/test/main_client.cpp
+++ b/libs/binder/ndk/test/main_client.cpp
@@ -134,3 +134,4 @@
#include <android/binder_auto_utils.h>
#include <android/binder_interface_utils.h>
+#include <android/binder_parcel_utils.h>
diff --git a/libs/binder/ndk/update.sh b/libs/binder/ndk/update.sh
index 49b4730..9a4577f 100755
--- a/libs/binder/ndk/update.sh
+++ b/libs/binder/ndk/update.sh
@@ -18,6 +18,6 @@
set -ex
# This script makes sure that the source code is in sync with the various scripts
-./scripts/init_map.sh > libbinder_ndk.map.txt
./scripts/gen_parcel_helper.py
./scripts/format.sh
+./scripts/init_map.sh > libbinder_ndk.map.txt
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 73c2eba..7bcfffd 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -71,6 +71,7 @@
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,
};
pid_t start_server_process(int arg2, bool usePoll = false)
@@ -938,6 +939,43 @@
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;
+ uid_t previousWorkSource = IPCThreadState::self()->setWorkSource(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(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceCleared)
+{
+ status_t ret;
+ Parcel data, reply;
+
+ IPCThreadState::self()->setWorkSource(100);
+ uid_t previousWorkSource = IPCThreadState::self()->clearWorkSource();
+ 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);
+}
+
class BinderLibTestService : public BBinder
{
public:
@@ -1236,6 +1274,11 @@
}
return NO_ERROR;
}
+ case BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION: {
+ data.enforceInterface(binderLibTestServiceName);
+ reply->writeInt32(IPCThreadState::self()->getWorkSource());
+ return NO_ERROR;
+ }
default:
return UNKNOWN_TRANSACTION;
};
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 4da30e9..bab87ac 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -23,6 +23,7 @@
shared_libs: [
"liblog",
+ "libcutils",
],
export_include_dirs: ["include"],
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 2a7d76e..024d72b 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -18,9 +18,12 @@
#define LOG_TAG "GraphicsEnv"
#include <graphicsenv/GraphicsEnv.h>
+#include <sys/prctl.h>
+
#include <mutex>
#include <android/dlext.h>
+#include <cutils/properties.h>
#include <log/log.h>
// TODO(b/37049319) Get this from a header once one exists
@@ -46,6 +49,14 @@
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'",
@@ -57,7 +68,9 @@
}
void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
- const std::string appPref, bool developerOptIn) {
+ const std::string appPref, bool developerOptIn,
+ const int rulesFd, const long rulesOffset,
+ const long rulesLength) {
if (!mAnglePath.empty()) {
ALOGV("ignoring attempt to change ANGLE path from '%s' to '%s'", mAnglePath.c_str(),
path.c_str());
@@ -83,6 +96,13 @@
}
mAngleDeveloperOptIn = developerOptIn;
+
+ ALOGV("setting ANGLE rules file descriptor to '%i'", rulesFd);
+ mAngleRulesFd = rulesFd;
+ ALOGV("setting ANGLE rules offset to '%li'", rulesOffset);
+ mAngleRulesOffset = rulesOffset;
+ ALOGV("setting ANGLE rules length to '%li'", rulesLength);
+ mAngleRulesLength = rulesLength;
}
void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) {
@@ -113,6 +133,18 @@
return mAngleAppPref.c_str();
}
+int GraphicsEnv::getAngleRulesFd() {
+ return mAngleRulesFd;
+}
+
+long GraphicsEnv::getAngleRulesOffset() {
+ return mAngleRulesOffset;
+}
+
+long GraphicsEnv::getAngleRulesLength() {
+ return mAngleRulesLength;
+}
+
const std::string GraphicsEnv::getLayerPaths(){
return mLayerPaths;
}
@@ -135,7 +167,7 @@
auto sphalNamespace = android_get_exported_namespace("sphal");
if (!sphalNamespace) 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,
@@ -181,4 +213,19 @@
const char* android_getAngleAppPref() {
return android::GraphicsEnv::getInstance().getAngleAppPref();
}
+int android_getAngleRulesFd() {
+ return android::GraphicsEnv::getInstance().getAngleRulesFd();
+}
+long android_getAngleRulesOffset() {
+ return android::GraphicsEnv::getInstance().getAngleRulesOffset();
+}
+long android_getAngleRulesLength() {
+ return android::GraphicsEnv::getInstance().getAngleRulesLength();
+}
+const char* android_getLayerPaths() {
+ return android::GraphicsEnv::getInstance().getLayerPaths().c_str();
+}
+const char* android_getDebugLayers() {
+ return android::GraphicsEnv::getInstance().getDebugLayers().c_str();
+}
}
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 00e8fc0..404823a 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -29,6 +29,8 @@
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
@@ -43,11 +45,15 @@
// 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, const std::string appPref,
- bool devOptIn);
+ bool devOptIn, const int rulesFd, const long rulesOffset,
+ const long rulesLength);
android_namespace_t* getAngleNamespace();
const char* getAngleAppName();
const char* getAngleAppPref();
bool getAngleDeveloperOptIn();
+ int getAngleRulesFd();
+ long getAngleRulesOffset();
+ long getAngleRulesLength();
void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths);
NativeLoaderNamespace* getAppNamespace();
@@ -64,6 +70,9 @@
std::string mAngleAppName;
std::string mAngleAppPref;
bool mAngleDeveloperOptIn;
+ int mAngleRulesFd;
+ long mAngleRulesOffset;
+ long mAngleRulesLength;
std::string mDebugLayers;
std::string mLayerPaths;
android_namespace_t* mDriverNamespace = nullptr;
@@ -85,11 +94,16 @@
* will be removed soon.
*/
extern "C" {
-android_namespace_t* android_getDriverNamespace();
-android_namespace_t* android_getAngleNamespace();
-const char* android_getAngleAppName();
-const char* android_getAngleAppPref();
-bool android_getAngleDeveloperOptIn();
+ android_namespace_t* android_getDriverNamespace();
+ android_namespace_t* android_getAngleNamespace();
+ const char* android_getAngleAppName();
+ const char* android_getAngleAppPref();
+ bool android_getAngleDeveloperOptIn();
+ int android_getAngleRulesFd();
+ long android_getAngleRulesOffset();
+ long android_getAngleRulesLength();
+ const char* android_getLayerPaths();
+ const char* android_getDebugLayers();
}
#endif // ANDROID_UI_GRAPHICS_ENV_H
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp
index 3dac037..1a7c2d3 100644
--- a/libs/gui/BufferHubProducer.cpp
+++ b/libs/gui/BufferHubProducer.cpp
@@ -400,19 +400,8 @@
ALOGE("attachBuffer: DetachedBufferHandle cannot be NULL.");
return BAD_VALUE;
}
- auto detached_buffer = BufferHubBuffer::Import(std::move(detached_handle->handle()));
- if (detached_buffer == nullptr) {
- ALOGE("attachBuffer: BufferHubBuffer cannot be NULL.");
- return BAD_VALUE;
- }
- auto status_or_handle = detached_buffer->Promote();
- if (!status_or_handle.ok()) {
- ALOGE("attachBuffer: Failed to promote a BufferHubBuffer into a BufferProducer, error=%d.",
- status_or_handle.error());
- return BAD_VALUE;
- }
std::shared_ptr<BufferProducer> buffer_producer =
- BufferProducer::Import(status_or_handle.take());
+ BufferProducer::Import(std::move(detached_handle->handle()));
if (buffer_producer == nullptr) {
ALOGE("attachBuffer: Failed to import BufferProducer.");
return BAD_VALUE;
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index f99bc53..cf9d4c5 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -103,11 +103,15 @@
}
virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- bool useIdentityTransform, ISurfaceComposer::Rotation rotation) {
+ 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);
@@ -126,15 +130,19 @@
*outBuffer = new GraphicBuffer();
reply.read(**outBuffer);
+
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);
@@ -148,6 +156,7 @@
ALOGE("captureLayers failed to readInt32: %d", result);
return result;
}
+
*outBuffer = new GraphicBuffer();
reply.read(**outBuffer);
@@ -645,6 +654,8 @@
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);
@@ -653,8 +664,8 @@
bool useIdentityTransform = static_cast<bool>(data.readInt32());
int32_t rotation = data.readInt32();
- status_t res = captureScreen(display, &outBuffer, sourceCrop, reqWidth, reqHeight,
- 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) {
@@ -665,14 +676,16 @@
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);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1ac9609..7498f36 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -887,13 +887,14 @@
// ----------------------------------------------------------------------------
-status_t ScreenshotClient::capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth,
- uint32_t reqHeight, 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 == nullptr) return NO_INIT;
- status_t ret = s->captureScreen(display, outBuffer, sourceCrop, reqWidth, reqHeight,
- useIdentityTransform,
+ status_t ret = s->captureScreen(display, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop,
+ reqWidth, reqHeight, useIdentityTransform,
static_cast<ISurfaceComposer::Rotation>(rotation));
if (ret != NO_ERROR) {
return ret;
@@ -901,21 +902,25 @@
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 == nullptr) return NO_INIT;
- status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale,
- false /* childrenOnly */);
+ 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 == nullptr) return NO_INIT;
- status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale,
- true /* childrenOnly */);
+ status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat,
+ sourceCrop, frameScale, true /* childrenOnly */);
return ret;
}
// ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index a55cfe0..781e062 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -180,6 +180,36 @@
* 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,
+ 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.
*
@@ -193,15 +223,34 @@
*/
virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- bool useIdentityTransform, Rotation rotation = eRotateNone) = 0;
+ 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.
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 69a759f..1b4eda7 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -329,12 +329,16 @@
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, 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);
};
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index bc80899..500df05 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -134,8 +134,9 @@
sp<IBinder> display(sf->getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(),
- 64, 64, 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));
@@ -165,8 +166,9 @@
&buf));
ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
}
- ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(),
- 64, 64, 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) {
@@ -596,14 +598,19 @@
}
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*/,
- 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; }
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 7b35409..0a0c8ca 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -78,7 +78,7 @@
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",
@@ -97,7 +97,7 @@
],
export_shared_lib_headers: [
- "android.hardware.graphics.common@1.1",
+ "android.hardware.graphics.common@1.2",
],
static_libs: [
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index 606386c..44a947e 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -41,7 +41,6 @@
#include <poll.h>
-using android::dvr::BufferHubMetadata;
using android::dvr::BufferTraits;
using android::dvr::DetachedBufferRPC;
using android::dvr::NativeHandleWrapper;
@@ -160,7 +159,7 @@
// If all imports succeed, replace the previous buffer and id.
mId = bufferId;
- mBfferStateBit = bufferTraits.buffer_state_bit();
+ mBufferStateBit = bufferTraits.buffer_state_bit();
// TODO(b/112012161) Set up shared fences.
ALOGD("BufferHubBuffer::ImportGraphicBuffer: id=%d, buffer_state=%" PRIx64 ".", id(),
@@ -175,26 +174,6 @@
return poll(&p, 1, timeoutMs);
}
-Status<LocalChannelHandle> BufferHubBuffer::Promote() {
- ATRACE_CALL();
-
- // TODO(b/112338294) remove after migrate producer buffer to binder
- ALOGW("BufferHubBuffer::Promote: not supported operation during migration");
- return {};
-
- ALOGD("BufferHubBuffer::Promote: id=%d.", mId);
-
- auto statusOrHandle = mClient.InvokeRemoteMethod<DetachedBufferRPC::Promote>();
- if (statusOrHandle.ok()) {
- // Invalidate the buffer.
- mBufferHandle = {};
- } else {
- ALOGE("BufferHubBuffer::Promote: Failed to promote buffer (id=%d): %s.", mId,
- statusOrHandle.GetErrorMessage().c_str());
- }
- return statusOrHandle;
-}
-
Status<LocalChannelHandle> BufferHubBuffer::Duplicate() {
ATRACE_CALL();
ALOGD("BufferHubBuffer::Duplicate: id=%d.", mId);
diff --git a/libs/ui/BufferHubMetadata.cpp b/libs/ui/BufferHubMetadata.cpp
index b0c4510..1e08ed1 100644
--- a/libs/ui/BufferHubMetadata.cpp
+++ b/libs/ui/BufferHubMetadata.cpp
@@ -22,7 +22,6 @@
#include <ui/BufferHubMetadata.h>
namespace android {
-namespace dvr {
namespace {
@@ -30,8 +29,8 @@
} // namespace
-using BufferHubDefs::kMetadataHeaderSize;
-using BufferHubDefs::MetadataHeader;
+using dvr::BufferHubDefs::kMetadataHeaderSize;
+using dvr::BufferHubDefs::MetadataHeader;
/* static */
BufferHubMetadata BufferHubMetadata::Create(size_t userMetadataSize) {
@@ -101,5 +100,4 @@
}
}
-} // namespace dvr
} // namespace android
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 61df02d..ee06d93 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -234,6 +234,9 @@
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/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
index e8ae244..cefe5b3 100644
--- a/libs/ui/include/ui/BufferHubBuffer.h
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -88,7 +88,7 @@
// 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 mBfferStateBit; }
+ uint64_t buffer_state_bit() const { return mBufferStateBit; }
size_t user_metadata_size() const { return mMetadata.user_metadata_size(); }
@@ -112,11 +112,6 @@
// Polls the fd for |timeoutMs| milliseconds (-1 for infinity).
int Poll(int timeoutMs);
- // Promotes a BufferHubBuffer to become a ProducerBuffer. Once promoted the BufferHubBuffer
- // channel will be closed automatically on successful IPC return. Further IPCs towards this
- // channel will return error.
- pdx::Status<pdx::LocalChannelHandle> Promote();
-
// 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();
@@ -130,15 +125,15 @@
int ImportGraphicBuffer();
// Global id for the buffer that is consistent across processes.
- int mId;
- uint64_t mBfferStateBit;
+ int mId = -1;
+ uint64_t mBufferStateBit = 0;
// 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.
- dvr::BufferHubMetadata mMetadata;
+ BufferHubMetadata mMetadata;
// PDX backend.
BufferHubClient mClient;
diff --git a/libs/ui/include/ui/BufferHubMetadata.h b/libs/ui/include/ui/BufferHubMetadata.h
index 46adc6a..94a9000 100644
--- a/libs/ui/include/ui/BufferHubMetadata.h
+++ b/libs/ui/include/ui/BufferHubMetadata.h
@@ -38,7 +38,6 @@
#pragma clang diagnostic pop
namespace android {
-namespace dvr {
class BufferHubMetadata {
public:
@@ -83,24 +82,25 @@
bool IsValid() const { return mAshmemHandle.IsValid() && mMetadataHeader != nullptr; }
size_t user_metadata_size() const { return mUserMetadataSize; }
- size_t metadata_size() const { return mUserMetadataSize + BufferHubDefs::kMetadataHeaderSize; }
+ size_t metadata_size() const {
+ return mUserMetadataSize + dvr::BufferHubDefs::kMetadataHeaderSize;
+ }
const pdx::LocalHandle& ashmem_handle() const { return mAshmemHandle; }
- BufferHubDefs::MetadataHeader* metadata_header() { return mMetadataHeader; }
+ dvr::BufferHubDefs::MetadataHeader* metadata_header() { return mMetadataHeader; }
private:
BufferHubMetadata(size_t userMetadataSize, pdx::LocalHandle ashmemHandle,
- BufferHubDefs::MetadataHeader* metadataHeader);
+ dvr::BufferHubDefs::MetadataHeader* metadataHeader);
BufferHubMetadata(const BufferHubMetadata&) = delete;
void operator=(const BufferHubMetadata&) = delete;
size_t mUserMetadataSize = 0;
pdx::LocalHandle mAshmemHandle;
- BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr;
+ dvr::BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr;
};
-} // namespace dvr
} // namespace android
#endif // ANDROID_BUFFER_HUB_METADATA_H_
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 6a7479a..4cd9a0b 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -28,7 +28,7 @@
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 @@
(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/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 0fa819d..1d53ac8 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
@@ -25,10 +26,10 @@
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;
} // namespace ui
} // namespace android
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 6e73960..228d202 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -36,6 +36,14 @@
}
cc_test {
+ name: "BufferHubBuffer_test",
+ header_libs: ["libbufferhub_headers", "libdvr_headers"],
+ shared_libs: ["libpdx_default_transport", "libui", "libutils"],
+ srcs: ["BufferHubBuffer_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "BufferHubMetadata_test",
header_libs: ["libbufferhub_headers", "libdvr_headers"],
shared_libs: ["libpdx_default_transport", "libui", "libutils"],
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
new file mode 100644
index 0000000..be06ad2
--- /dev/null
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 <gtest/gtest.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;
+
+} // namespace
+
+using BufferHubBufferTest = ::testing::Test;
+
+using dvr::BufferHubDefs::IsBufferGained;
+using dvr::BufferHubDefs::kMetadataHeaderSize;
+using dvr::BufferHubDefs::kProducerStateBit;
+using pdx::LocalChannelHandle;
+
+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();
+ 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();
+
+ // 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 buffer_state_bit() to tell those two instances apart.
+ EXPECT_NE(b1->buffer_state_bit(), b2->buffer_state_bit());
+ EXPECT_NE(b1->buffer_state_bit(), 0ULL);
+ EXPECT_NE(b2->buffer_state_bit(), 0ULL);
+ EXPECT_NE(b1->buffer_state_bit(), kProducerStateBit);
+ EXPECT_NE(b2->buffer_state_bit(), kProducerStateBit);
+
+ // Both buffer instances should be in gained state.
+ EXPECT_TRUE(IsBufferGained(b1->buffer_state()));
+ EXPECT_TRUE(IsBufferGained(b2->buffer_state()));
+
+ // TODO(b/112338294): rewrite test after migration
+ return;
+}
+
+} // namespace android
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index ba5cfcd..43ac79e 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -22,6 +22,7 @@
"buffer_hub_base.cpp",
"buffer_hub_rpc.cpp",
"consumer_buffer.cpp",
+ "buffer_client_impl.cpp",
"ion_buffer.cpp",
"producer_buffer.cpp",
]
diff --git a/libs/vr/libbufferhub/buffer_client_impl.cpp b/libs/vr/libbufferhub/buffer_client_impl.cpp
new file mode 100644
index 0000000..6bef98a
--- /dev/null
+++ b/libs/vr/libbufferhub/buffer_client_impl.cpp
@@ -0,0 +1,55 @@
+#include <log/log.h>
+#include <private/dvr/IBufferClient.h>
+
+namespace android {
+namespace dvr {
+
+class BpBufferClient : public BpInterface<IBufferClient> {
+ public:
+ explicit BpBufferClient(const sp<IBinder>& impl)
+ : BpInterface<IBufferClient>(impl) {}
+
+ bool isValid() override;
+};
+
+IMPLEMENT_META_INTERFACE(BufferClient, "android.dvr.IBufferClient");
+
+// Transaction code
+enum {
+ IS_VALID = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+bool BpBufferClient::isValid() {
+ Parcel data, reply;
+ status_t ret =
+ data.writeInterfaceToken(IBufferClient::getInterfaceDescriptor());
+ if (ret != NO_ERROR) {
+ ALOGE("BpBufferClient::isValid: failed to write into parcel; errno=%d",
+ ret);
+ return false;
+ }
+
+ ret = remote()->transact(IS_VALID, data, &reply);
+ if (ret == NO_ERROR) {
+ return reply.readBool();
+ } else {
+ ALOGE("BpBufferClient::isValid: failed to transact; errno=%d", ret);
+ return false;
+ }
+}
+
+status_t BnBufferClient::onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags) {
+ switch (code) {
+ case IS_VALID: {
+ CHECK_INTERFACE(IBufferClient, data, reply);
+ return reply->writeBool(isValid());
+ } break;
+ default:
+ // Should not reach except binder defined transactions such as dumpsys
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+} // namespace dvr
+} // namespace android
\ No newline at end of file
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index b477f1a..4d98dfc 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -214,13 +214,12 @@
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
- uint64_t context;
LocalHandle fence;
// 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->Acquire(&fence));
EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
EXPECT_EQ(-EALREADY, p->Gain(&fence));
@@ -233,10 +232,10 @@
EXPECT_EQ(-EBUSY, p->Gain(&fence));
// Acquire in posted state should succeed.
- EXPECT_LE(0, c->Acquire(&fence, &context));
+ EXPECT_LE(0, c->Acquire(&fence));
// Acquire, post, and gain in acquired state should fail.
- EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context));
+ EXPECT_EQ(-EBUSY, c->Acquire(&fence));
EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
EXPECT_EQ(-EBUSY, p->Gain(&fence));
@@ -246,14 +245,14 @@
// 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, 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->Acquire(&fence));
EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
EXPECT_EQ(-EALREADY, p->Gain(&fence));
}
@@ -491,7 +490,7 @@
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()));
@@ -552,11 +551,11 @@
// 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);
}
@@ -829,43 +828,7 @@
EXPECT_NE(b1->id(), 0);
}
-TEST_F(LibBufferHubTest, TestPromoteBufferHubBuffer) {
- // TODO(b/112338294) rewrite test after migration
- return;
-
- auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat,
- kUsage, kUserMetadataSize);
- int b1_id = b1->id();
- EXPECT_TRUE(b1->IsValid());
-
- 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<ProducerBuffer> p1 = ProducerBuffer::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) {
+TEST_F(LibBufferHubTest, TestDetach) {
// TODO(b/112338294) rewrite test after migration
return;
@@ -887,26 +850,6 @@
EXPECT_TRUE(b1->IsValid());
int b1_id = b1->id();
EXPECT_EQ(b1_id, p1_id);
-
- // Promote the detached buffer.
- status_or_handle = b1->Promote();
- // The buffer client is still considered as connected but invalid.
- EXPECT_TRUE(b1->IsConnected());
- EXPECT_FALSE(b1->IsValid());
- EXPECT_TRUE(status_or_handle.ok());
-
- // Gets the channel handle for the producer.
- LocalChannelHandle h2 = status_or_handle.take();
- EXPECT_TRUE(h2.valid());
-
- std::unique_ptr<ProducerBuffer> p2 = ProducerBuffer::Import(std::move(h2));
- EXPECT_FALSE(h2.valid());
- ASSERT_TRUE(p2 != nullptr);
- int p2_id = p2->id();
-
- // A newly promoted ProducerBuffer should inherit the same buffer id.
- EXPECT_EQ(b1_id, p2_id);
- EXPECT_TRUE(IsBufferGained(p2->buffer_state()));
}
TEST_F(LibBufferHubTest, TestDuplicateBufferHubBuffer) {
@@ -951,10 +894,4 @@
// TODO(b/112338294) rewrite test after migration
return;
-
- // Promote the detached buffer should fail as b1 is no longer the exclusive
- // owner of the buffer..
- status_or_handle = b1->Promote();
- EXPECT_FALSE(status_or_handle.ok());
- EXPECT_EQ(status_or_handle.error(), EINVAL);
}
diff --git a/libs/vr/libbufferhub/include/private/dvr/IBufferClient.h b/libs/vr/libbufferhub/include/private/dvr/IBufferClient.h
new file mode 100644
index 0000000..03f2d95
--- /dev/null
+++ b/libs/vr/libbufferhub/include/private/dvr/IBufferClient.h
@@ -0,0 +1,29 @@
+#ifndef ANDROID_DVR_IBUFFERCLIENT_H
+#define ANDROID_DVR_IBUFFERCLIENT_H
+
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+namespace dvr {
+
+// Interface for acessing BufferHubBuffer remotely.
+class IBufferClient : public IInterface {
+ public:
+ DECLARE_META_INTERFACE(BufferClient);
+
+ // Checks if the buffer node is valid.
+ virtual bool isValid() = 0;
+};
+
+// BnInterface for IBufferClient. Should only be created in bufferhub service.
+class BnBufferClient : public BnInterface<IBufferClient> {
+ public:
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0);
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_IBUFFERCLIENT_H
\ No newline at end of file
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
index 6d28d41..49f9c3e 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
@@ -191,11 +191,6 @@
// Imports the given channel handle to a DetachedBuffer, taking ownership.
kOpImport,
- // 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.
- kOpPromote,
-
// Creates a DetachedBuffer client from an existing one. The new client will
// share the same underlying gralloc buffer and ashmem region for metadata.
kOpDuplicate,
@@ -212,10 +207,9 @@
uint32_t format, uint64_t usage,
size_t user_metadata_size));
PDX_REMOTE_METHOD(Import, kOpImport, BufferTraits<LocalHandle>(Void));
- PDX_REMOTE_METHOD(Promote, kOpPromote, LocalChannelHandle(Void));
PDX_REMOTE_METHOD(Duplicate, kOpDuplicate, LocalChannelHandle(Void));
- PDX_REMOTE_API(API, Create, Import, Promote, Duplicate);
+ PDX_REMOTE_API(API, Create, Import, Duplicate);
};
} // namespace dvr
diff --git a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
index 2044c53..7349779 100644
--- a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
+++ b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
@@ -45,15 +45,6 @@
// 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);
diff --git a/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h
index 3086982..ae70b77 100644
--- a/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h
+++ b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h
@@ -40,7 +40,7 @@
bool IsValid() const { return ints_.size() != 0 || fds_.size() != 0; }
// Duplicate a native handle from the wrapper.
- const native_handle_t* DuplicateHandle() const {
+ native_handle_t* DuplicateHandle() const {
if (!IsValid()) {
return nullptr;
}
@@ -58,7 +58,7 @@
}
// Takes the native handle out of the wrapper.
- const native_handle_t* TakeHandle() {
+ native_handle_t* TakeHandle() {
if (!IsValid()) {
return nullptr;
}
@@ -70,8 +70,8 @@
NativeHandleWrapper(const NativeHandleWrapper&) = delete;
void operator=(const NativeHandleWrapper&) = delete;
- static const native_handle_t* FromFdsAndInts(std::vector<FileHandleType> fds,
- std::vector<int> ints) {
+ 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.");
diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h
index 93a3965..c9e8b7c 100644
--- a/opengl/include/EGL/egl.h
+++ b/opengl/include/EGL/egl.h
@@ -33,12 +33,12 @@
** used to make the header, and the header can be found at
** http://www.khronos.org/registry/egl
**
-** Khronos $Git commit SHA1: a732b061e7 $ on $Git commit date: 2017-06-17 23:27:53 +0100 $
+** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $
*/
#include <EGL/eglplatform.h>
-/* Generated on date 20170627 */
+/* Generated on date 20180517 */
/* Generated C header for:
* API: egl
@@ -235,10 +235,66 @@
EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext (void);
#endif /* EGL_VERSION_1_4 */
-/* This version of Android does not yet support EGL 1.5, but the following
- * portion of EGL 1.5 is included in order to support portions of "eglext.h".
- */
+#ifndef EGL_VERSION_1_5
+#define EGL_VERSION_1_5 1
+typedef void *EGLSync;
typedef intptr_t EGLAttrib;
+typedef khronos_utime_nanoseconds_t EGLTime;
+typedef void *EGLImage;
+#define EGL_CONTEXT_MAJOR_VERSION 0x3098
+#define EGL_CONTEXT_MINOR_VERSION 0x30FB
+#define EGL_CONTEXT_OPENGL_PROFILE_MASK 0x30FD
+#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY 0x31BD
+#define EGL_NO_RESET_NOTIFICATION 0x31BE
+#define EGL_LOSE_CONTEXT_ON_RESET 0x31BF
+#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001
+#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT 0x00000002
+#define EGL_CONTEXT_OPENGL_DEBUG 0x31B0
+#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE 0x31B1
+#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS 0x31B2
+#define EGL_OPENGL_ES3_BIT 0x00000040
+#define EGL_CL_EVENT_HANDLE 0x309C
+#define EGL_SYNC_CL_EVENT 0x30FE
+#define EGL_SYNC_CL_EVENT_COMPLETE 0x30FF
+#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE 0x30F0
+#define EGL_SYNC_TYPE 0x30F7
+#define EGL_SYNC_STATUS 0x30F1
+#define EGL_SYNC_CONDITION 0x30F8
+#define EGL_SIGNALED 0x30F2
+#define EGL_UNSIGNALED 0x30F3
+#define EGL_SYNC_FLUSH_COMMANDS_BIT 0x0001
+#define EGL_FOREVER 0xFFFFFFFFFFFFFFFFull
+#define EGL_TIMEOUT_EXPIRED 0x30F5
+#define EGL_CONDITION_SATISFIED 0x30F6
+#define EGL_NO_SYNC EGL_CAST(EGLSync,0)
+#define EGL_SYNC_FENCE 0x30F9
+#define EGL_GL_COLORSPACE 0x309D
+#define EGL_GL_COLORSPACE_SRGB 0x3089
+#define EGL_GL_COLORSPACE_LINEAR 0x308A
+#define EGL_GL_RENDERBUFFER 0x30B9
+#define EGL_GL_TEXTURE_2D 0x30B1
+#define EGL_GL_TEXTURE_LEVEL 0x30BC
+#define EGL_GL_TEXTURE_3D 0x30B2
+#define EGL_GL_TEXTURE_ZOFFSET 0x30BD
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x30B3
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x30B4
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x30B5
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x30B6
+#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x30B7
+#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8
+#define EGL_IMAGE_PRESERVED 0x30D2
+#define EGL_NO_IMAGE EGL_CAST(EGLImage,0)
+EGLAPI EGLSync EGLAPIENTRY eglCreateSync (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroySync (EGLDisplay dpy, EGLSync sync);
+EGLAPI EGLint EGLAPIENTRY eglClientWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
+EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttrib (EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value);
+EGLAPI EGLImage EGLAPIENTRY eglCreateImage (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImage (EGLDisplay dpy, EGLImage image);
+EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplay (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
+EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurface (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags);
+#endif /* EGL_VERSION_1_5 */
#ifdef __cplusplus
}
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 0fd91eb..ad4ffd7 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -33,12 +33,12 @@
** used to make the header, and the header can be found at
** http://www.khronos.org/registry/egl
**
-** Khronos $Git commit SHA1: feaaeb19e1 $ on $Git commit date: 2018-02-26 20:49:02 -0800 $
+** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $
*/
#include <EGL/eglplatform.h>
-#define EGL_EGLEXT_VERSION 20180228
+#define EGL_EGLEXT_VERSION 20180517
/* Generated C header for:
* API: egl
@@ -618,6 +618,16 @@
#define EGL_EXT_client_extensions 1
#endif /* EGL_EXT_client_extensions */
+#ifndef EGL_EXT_client_sync
+#define EGL_EXT_client_sync 1
+#define EGL_SYNC_CLIENT_EXT 0x3364
+#define EGL_SYNC_CLIENT_SIGNAL_EXT 0x3365
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLCLIENTSIGNALSYNCEXTPROC) (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglClientSignalSyncEXT (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#endif
+#endif /* EGL_EXT_client_sync */
+
#ifndef EGL_EXT_compositor
#define EGL_EXT_compositor 1
#define EGL_PRIMARY_COMPOSITOR_CONTEXT_EXT 0x3460
@@ -903,6 +913,14 @@
#endif
#endif /* EGL_EXT_swap_buffers_with_damage */
+#ifndef EGL_EXT_sync_reuse
+#define EGL_EXT_sync_reuse 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNSIGNALSYNCEXTPROC) (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglUnsignalSyncEXT (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list);
+#endif
+#endif /* EGL_EXT_sync_reuse */
+
#ifndef EGL_EXT_yuv_surface
#define EGL_EXT_yuv_surface 1
#define EGL_YUV_ORDER_EXT 0x3301
@@ -1147,6 +1165,14 @@
#define EGL_STREAM_FIFO_SYNCHRONOUS_NV 0x3336
#endif /* EGL_NV_stream_fifo_synchronous */
+#ifndef EGL_NV_stream_flush
+#define EGL_NV_stream_flush 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMFLUSHNVPROC) (EGLDisplay dpy, EGLStreamKHR stream);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamFlushNV (EGLDisplay dpy, EGLStreamKHR stream);
+#endif
+#endif /* EGL_NV_stream_flush */
+
#ifndef EGL_NV_stream_frame_limits
#define EGL_NV_stream_frame_limits 1
#define EGL_PRODUCER_MAX_FRAME_HINT_NV 0x3337
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index c77c333..0bc2cb9 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -77,12 +77,24 @@
typedef HBITMAP EGLNativePixmapType;
typedef HWND EGLNativeWindowType;
-#elif defined(__APPLE__) || defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */
+#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */
typedef int EGLNativeDisplayType;
typedef void *EGLNativeWindowType;
typedef void *EGLNativePixmapType;
+#elif defined(WL_EGL_PLATFORM)
+
+typedef struct wl_display *EGLNativeDisplayType;
+typedef struct wl_egl_pixmap *EGLNativePixmapType;
+typedef struct wl_egl_window *EGLNativeWindowType;
+
+#elif defined(__GBM__)
+
+typedef struct gbm_device *EGLNativeDisplayType;
+typedef struct gbm_bo *EGLNativePixmapType;
+typedef void *EGLNativeWindowType;
+
#elif defined(__ANDROID__) || defined(ANDROID)
struct ANativeWindow;
@@ -92,7 +104,13 @@
typedef struct egl_native_pixmap_t* EGLNativePixmapType;
typedef void* EGLNativeDisplayType;
-#elif defined(__unix__)
+#elif defined(USE_OZONE)
+
+typedef intptr_t EGLNativeDisplayType;
+typedef intptr_t EGLNativeWindowType;
+typedef intptr_t EGLNativePixmapType;
+
+#elif defined(__unix__) || defined(USE_X11)
/* X11 (tentative) */
#include <X11/Xlib.h>
@@ -102,6 +120,20 @@
typedef Pixmap EGLNativePixmapType;
typedef Window EGLNativeWindowType;
+#elif defined(__APPLE__)
+
+typedef int EGLNativeDisplayType;
+typedef void *EGLNativeWindowType;
+typedef void *EGLNativePixmapType;
+
+#elif defined(__HAIKU__)
+
+#include <kernel/image.h>
+
+typedef void *EGLNativeDisplayType;
+typedef khronos_uintptr_t EGLNativePixmapType;
+typedef khronos_uintptr_t EGLNativeWindowType;
+
#else
#error "Platform not recognized"
#endif
diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp
index 1d5def5..8bb7e83 100644
--- a/opengl/libagl/state.cpp
+++ b/opengl/libagl/state.cpp
@@ -181,7 +181,7 @@
case GL_FOG:
case GL_DEPTH_TEST:
ogles_invalidate_perspective(c);
- // fall-through...
+ [[fallthrough]];
case GL_BLEND:
case GL_SCISSOR_TEST:
case GL_ALPHA_TEST:
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index aae8e05..4c5f3e9 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -401,14 +401,14 @@
switch (format) {
case GL_PALETTE4_RGB8_OES:
indexBits = 4;
- /* FALLTHROUGH */
+ [[fallthrough]];
case GL_PALETTE8_RGB8_OES:
entrySize = 3;
break;
case GL_PALETTE4_RGBA8_OES:
indexBits = 4;
- /* FALLTHROUGH */
+ [[fallthrough]];
case GL_PALETTE8_RGBA8_OES:
entrySize = 4;
break;
@@ -417,7 +417,7 @@
case GL_PALETTE4_RGBA4_OES:
case GL_PALETTE4_RGB5_A1_OES:
indexBits = 4;
- /* FALLTHROUGH */
+ [[fallthrough]];
case GL_PALETTE8_R5_G6_B5_OES:
case GL_PALETTE8_RGBA4_OES:
case GL_PALETTE8_RGB5_A1_OES:
@@ -446,14 +446,14 @@
switch (format) {
case GL_PALETTE4_RGB8_OES:
indexBits = 4;
- /* FALLTHROUGH */
+ [[fallthrough]];
case GL_PALETTE8_RGB8_OES:
entrySize = 3;
break;
case GL_PALETTE4_RGBA8_OES:
indexBits = 4;
- /* FALLTHROUGH */
+ [[fallthrough]];
case GL_PALETTE8_RGBA8_OES:
entrySize = 4;
break;
@@ -462,7 +462,7 @@
case GL_PALETTE4_RGBA4_OES:
case GL_PALETTE4_RGB5_A1_OES:
indexBits = 4;
- /* FALLTHROUGH */
+ [[fallthrough]];
case GL_PALETTE8_R5_G6_B5_OES:
case GL_PALETTE8_RGBA4_OES:
case GL_PALETTE8_RGB5_A1_OES:
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 2a6dee4..583aec9 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -140,8 +140,10 @@
"EGL/egl_cache.cpp",
"EGL/egl_display.cpp",
"EGL/egl_object.cpp",
+ "EGL/egl_layers.cpp",
"EGL/egl.cpp",
"EGL/eglApi.cpp",
+ "EGL/egl_platform_entries.cpp",
"EGL/Loader.cpp",
"EGL/egl_angle_platform.cpp",
],
@@ -149,8 +151,11 @@
"libvndksupport",
"android.hardware.configstore@1.0",
"android.hardware.configstore-utils",
+ "libbase",
"libhidlbase",
"libhidltransport",
+ "libnativebridge",
+ "libnativeloader",
"libutils",
],
static_libs: [
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 87f0fe1..a7fea33 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -33,6 +33,7 @@
#endif
#include <vndksupport/linker.h>
+#include "egl_platform_entries.h"
#include "egl_trace.h"
#include "egldefs.h"
@@ -50,6 +51,14 @@
typedef bool (*fpANGLEUseForApplication)(const char* appName, const char* deviceMfr,
const char* deviceModel, ANGLEPreference developerOption,
ANGLEPreference appPreference);
+
+ // TODO(ianelliott@): Get this from an ANGLE header:
+ typedef bool (*fpANGLEGetUtilityAPI)(unsigned int* versionToUse);
+
+ // TODO(ianelliott@): Get this from an ANGLE header:
+ typedef bool (*fpAndroidUseANGLEForApplication)(int fd, long offset, long length,
+ const char* appName, const char* deviceMfr,
+ const char* deviceModel);
}
// ----------------------------------------------------------------------------
@@ -510,7 +519,7 @@
return ANGLE_NO_PREFERENCE;
}
-static void* load_angle(const char* kind, egl_connection_t* cnx) {
+static void* load_angle(const char* kind, android_namespace_t* ns, egl_connection_t* cnx) {
// Only attempt to load ANGLE libs
if (strcmp(kind, "EGL") != 0 && strcmp(kind, "GLESv2") != 0 && strcmp(kind, "GLESv1_CM") != 0)
return nullptr;
@@ -519,10 +528,12 @@
std::string name;
char prop[PROPERTY_VALUE_MAX];
- android_namespace_t* ns = android_getAngleNamespace();
const char* app_name = android_getAngleAppName();
const char* app_pref = android_getAngleAppPref();
bool developer_opt_in = android_getAngleDeveloperOptIn();
+ const int rules_fd = android_getAngleRulesFd();
+ const long rules_offset = android_getAngleRulesOffset();
+ const long rules_length = android_getAngleRulesLength();
// Determine whether or not to use ANGLE:
ANGLEPreference developer_option = developer_opt_in ? ANGLE_PREFER_ANGLE : ANGLE_NO_PREFERENCE;
@@ -538,21 +549,58 @@
char model[PROPERTY_VALUE_MAX];
property_get("ro.product.manufacturer", manufacturer, "UNSET");
property_get("ro.product.model", model, "UNSET");
- ANGLEPreference app_preference = getAnglePref(android_getAngleAppPref());
- so = load_angle_from_namespace("feature_support", ns);
+ // Check if ANGLE is enabled. Workaround for b/118375731
+ // We suspect that loading & unloading a library somehow corrupts
+ // the process.
+ property_get("debug.angle.enable", prop, "0");
+ if (atoi(prop)) {
+ so = load_angle_from_namespace("feature_support", ns);
+ }
if (so) {
ALOGV("Temporarily loaded ANGLE's opt-in/out logic from namespace");
- fpANGLEUseForApplication fp =
- (fpANGLEUseForApplication)dlsym(so, "ANGLEUseForApplication");
- if (fp) {
- use_angle = (fp)(app_name_str.c_str(), manufacturer, model, developer_option,
- app_preference);
- ALOGV("Result of opt-in/out logic is %s", use_angle ? "true" : "false");
+ bool use_version0_API = false;
+ bool use_version1_API = false;
+ fpANGLEGetUtilityAPI ANGLEGetUtilityAPI =
+ (fpANGLEGetUtilityAPI)dlsym(so, "ANGLEGetUtilityAPI");
+ if (ANGLEGetUtilityAPI) {
+ unsigned int versionToUse = 1;
+ if ((ANGLEGetUtilityAPI)(&versionToUse)) {
+ if (versionToUse == 1) {
+ use_version1_API = true;
+ } else {
+ use_version0_API = true;
+ }
+ }
} else {
- ALOGW("Cannot find ANGLEUseForApplication in library");
+ use_version0_API = true;
}
-
+ if (use_version1_API) {
+ // Use the new version 1 API to determine if the
+ // application should use the ANGLE or the native driver.
+ fpAndroidUseANGLEForApplication AndroidUseANGLEForApplication =
+ (fpAndroidUseANGLEForApplication)dlsym(so, "AndroidUseANGLEForApplication");
+ if (AndroidUseANGLEForApplication) {
+ use_angle = (AndroidUseANGLEForApplication)(rules_fd, rules_offset,
+ rules_length, app_name_str.c_str(),
+ manufacturer, model);
+ } else {
+ ALOGW("Cannot find AndroidUseANGLEForApplication in library");
+ }
+ } else if (use_version0_API) {
+ // Use the old version 0 API to determine if the
+ // application should use the ANGLE or the native driver.
+ fpANGLEUseForApplication ANGLEUseForApplication =
+ (fpANGLEUseForApplication)dlsym(so, "ANGLEUseForApplication");
+ if (ANGLEUseForApplication) {
+ ANGLEPreference app_preference = getAnglePref(android_getAngleAppPref());
+ use_angle = (ANGLEUseForApplication)(app_name_str.c_str(), manufacturer, model,
+ developer_option, app_preference);
+ ALOGV("Result of opt-in/out logic is %s", use_angle ? "true" : "false");
+ } else {
+ ALOGW("Cannot find ANGLEUseForApplication in library");
+ }
+ }
ALOGV("Close temporarily-loaded ANGLE opt-in/out logic");
dlclose(so);
so = nullptr;
@@ -619,7 +667,10 @@
ATRACE_CALL();
void* dso = nullptr;
- dso = load_angle(kind, cnx);
+ android_namespace_t* ns = android_getAngleNamespace();
+ if (ns) {
+ dso = load_angle(kind, ns, cnx);
+ }
#ifndef __ANDROID_VNDK__
if (!dso) {
android_namespace_t* ns = android_getDriverNamespace();
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index e88d1a2..9cc73f3 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -33,7 +33,8 @@
enum {
EGL = 0x01,
GLESv1_CM = 0x02,
- GLESv2 = 0x04
+ GLESv2 = 0x04,
+ PLATFORM = 0x08
};
struct driver_t {
explicit driver_t(void* gles);
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 7089860..8870d5f 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -30,11 +30,10 @@
#include "egl_tls.h"
#include "egl_display.h"
#include "egl_object.h"
+#include "egl_layers.h"
#include "CallStack.h"
#include "Loader.h"
-typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
-
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
@@ -167,6 +166,10 @@
return (GLint)c->tokenized_gl_extensions.size();
}
+egl_connection_t* egl_get_connection() {
+ return &gEGLImpl;
+}
+
// ----------------------------------------------------------------------------
// this mutex protects:
@@ -192,6 +195,14 @@
cnx->dso = loader.open(cnx);
}
+ // Check to see if any layers are enabled and route functions through them
+ if (cnx->dso) {
+ // Layers can be enabled long after the drivers have been loaded.
+ // They will only be initialized once.
+ LayerLoader& layer_loader(LayerLoader::getInstance());
+ layer_loader.InitLayers(cnx);
+ }
+
return cnx->dso ? EGL_TRUE : EGL_FALSE;
}
@@ -262,6 +273,11 @@
nullptr
};
+char const * const platform_names[] = {
+ #include "platform_entries.in"
+ nullptr
+};
+
#undef GL_ENTRY
#undef EGL_ENTRY
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index d2dc514..29a966d 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1,5 +1,5 @@
/*
- ** Copyright 2007, The Android Open Source Project
+ ** 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.
@@ -16,1185 +16,225 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <ctype.h>
-#include <dlfcn.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <hardware/gralloc1.h>
-
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include <EGL/eglext_angle.h>
-
-#include <android/hardware_buffer.h>
-#include <private/android/AHardwareBufferHelpers.h>
-
-#include <cutils/compiler.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-
-#include <condition_variable>
-#include <deque>
-#include <mutex>
-#include <unordered_map>
-#include <string>
-#include <thread>
#include "../egl_impl.h"
-#include "egl_display.h"
-#include "egl_object.h"
+#include "egl_layers.h"
+#include "egl_platform_entries.h"
#include "egl_tls.h"
#include "egl_trace.h"
using namespace android;
-// ----------------------------------------------------------------------------
-
namespace android {
-using nsecs_t = int64_t;
+extern EGLBoolean egl_init_drivers();
-struct extention_map_t {
- const char* name;
- __eglMustCastToProperFunctionPointerType address;
-};
+} // namespace android
-/*
- * This is the list of EGL extensions exposed to applications.
- *
- * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL
- * wrapper and are always available.
- *
- * The rest (gExtensionString) depend on support in the EGL driver, and are
- * only available if the driver supports them. However, some of these must be
- * supported because they are used by the Android system itself; these are
- * listed as mandatory below and are required by the CDD. The system *assumes*
- * the mandatory extensions are present and may not function properly if some
- * are missing.
- *
- * NOTE: Both strings MUST have a single space as the last character.
- */
-
-extern char const * const gBuiltinExtensionString;
-extern char const * const gExtensionString;
-
-// clang-format off
-// Extensions implemented by the EGL wrapper.
-char const * const gBuiltinExtensionString =
- "EGL_KHR_get_all_proc_addresses "
- "EGL_ANDROID_presentation_time "
- "EGL_KHR_swap_buffers_with_damage "
- "EGL_ANDROID_get_native_client_buffer "
- "EGL_ANDROID_front_buffer_auto_refresh "
- "EGL_ANDROID_get_frame_timestamps "
- "EGL_EXT_surface_SMPTE2086_metadata "
- "EGL_EXT_surface_CTA861_3_metadata "
- ;
-
-// Whitelist of extensions exposed to applications if implemented in the vendor driver.
-char const * const gExtensionString =
- "EGL_KHR_image " // mandatory
- "EGL_KHR_image_base " // mandatory
- "EGL_EXT_image_gl_colorspace "
- "EGL_KHR_image_pixmap "
- "EGL_KHR_lock_surface "
- "EGL_KHR_gl_colorspace "
- "EGL_KHR_gl_texture_2D_image "
- "EGL_KHR_gl_texture_3D_image "
- "EGL_KHR_gl_texture_cubemap_image "
- "EGL_KHR_gl_renderbuffer_image "
- "EGL_KHR_reusable_sync "
- "EGL_KHR_fence_sync "
- "EGL_KHR_create_context "
- "EGL_KHR_config_attribs "
- "EGL_KHR_surfaceless_context "
- "EGL_KHR_stream "
- "EGL_KHR_stream_fifo "
- "EGL_KHR_stream_producer_eglsurface "
- "EGL_KHR_stream_consumer_gltexture "
- "EGL_KHR_stream_cross_process_fd "
- "EGL_EXT_create_context_robustness "
- "EGL_NV_system_time "
- "EGL_ANDROID_image_native_buffer " // mandatory
- "EGL_KHR_wait_sync " // strongly recommended
- "EGL_ANDROID_recordable " // mandatory
- "EGL_KHR_partial_update " // strongly recommended
- "EGL_EXT_pixel_format_float "
- "EGL_EXT_buffer_age " // strongly recommended with partial_update
- "EGL_KHR_create_context_no_error "
- "EGL_KHR_mutable_render_buffer "
- "EGL_EXT_yuv_surface "
- "EGL_EXT_protected_content "
- "EGL_IMG_context_priority "
- "EGL_KHR_no_config_context "
- ;
-// clang-format on
-
-// extensions not exposed to applications but used by the ANDROID system
-// "EGL_ANDROID_blob_cache " // strongly recommended
-// "EGL_IMG_hibernate_process " // optional
-// "EGL_ANDROID_native_fence_sync " // strongly recommended
-// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1
-
-/*
- * EGL Extensions entry-points exposed to 3rd party applications
- * (keep in sync with gExtensionString above)
- *
- */
-static const extention_map_t sExtensionMap[] = {
- // EGL_KHR_lock_surface
- { "eglLockSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
- { "eglUnlockSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
-
- // EGL_KHR_image, EGL_KHR_image_base
- { "eglCreateImageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
- { "eglDestroyImageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
-
- // EGL_KHR_reusable_sync, EGL_KHR_fence_sync
- { "eglCreateSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
- { "eglDestroySyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
- { "eglClientWaitSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
- { "eglSignalSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
- { "eglGetSyncAttribKHR",
- (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
-
- // EGL_NV_system_time
- { "eglGetSystemTimeFrequencyNV",
- (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
- { "eglGetSystemTimeNV",
- (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
-
- // EGL_KHR_wait_sync
- { "eglWaitSyncKHR",
- (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
-
- // EGL_ANDROID_presentation_time
- { "eglPresentationTimeANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
-
- // EGL_KHR_swap_buffers_with_damage
- { "eglSwapBuffersWithDamageKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
-
- // EGL_ANDROID_get_native_client_buffer
- { "eglGetNativeClientBufferANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
-
- // EGL_KHR_partial_update
- { "eglSetDamageRegionKHR",
- (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
-
- { "eglCreateStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
- { "eglDestroyStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
- { "eglStreamAttribKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
- { "eglQueryStreamKHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
- { "eglQueryStreamu64KHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
- { "eglQueryStreamTimeKHR",
- (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
- { "eglCreateStreamProducerSurfaceKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
- { "eglStreamConsumerGLTextureExternalKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
- { "eglStreamConsumerAcquireKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
- { "eglStreamConsumerReleaseKHR",
- (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
- { "eglGetStreamFileDescriptorKHR",
- (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
- { "eglCreateStreamFromFileDescriptorKHR",
- (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
-
- // EGL_ANDROID_get_frame_timestamps
- { "eglGetNextFrameIdANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
- { "eglGetCompositorTimingANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
- { "eglGetCompositorTimingSupportedANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
- { "eglGetFrameTimestampsANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
- { "eglGetFrameTimestampSupportedANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
-
- // EGL_ANDROID_native_fence_sync
- { "eglDupNativeFenceFDANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
-};
-
-/*
- * These extensions entry-points should not be exposed to applications.
- * They're used internally by the Android EGL layer.
- */
-#define FILTER_EXTENSIONS(procname) \
- (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \
- !strcmp((procname), "eglHibernateProcessIMG") || \
- !strcmp((procname), "eglAwakenProcessIMG"))
-
-// accesses protected by sExtensionMapMutex
-static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
-
-static int sGLExtentionSlot = 0;
-static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
-
-static void(*findProcAddress(const char* name,
- const extention_map_t* map, size_t n))() {
- for (uint32_t i=0 ; i<n ; i++) {
- if (!strcmp(name, map[i].name)) {
- return map[i].address;
- }
- }
- return nullptr;
+static inline void clearError() {
+ egl_tls_t::clearError();
}
-// ----------------------------------------------------------------------------
-
-extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
-extern EGLBoolean egl_init_drivers();
-extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
-extern gl_hooks_t gHooksTrace;
-
-} // namespace android;
-
-
-// ----------------------------------------------------------------------------
-
-static inline void clearError() { egl_tls_t::clearError(); }
-static inline EGLContext getContext() { return egl_tls_t::getContext(); }
-
-// ----------------------------------------------------------------------------
-
-EGLDisplay eglGetDisplay(EGLNativeDisplayType display)
-{
+EGLDisplay eglGetDisplay(EGLNativeDisplayType display) {
ATRACE_CALL();
clearError();
- uintptr_t index = reinterpret_cast<uintptr_t>(display);
- if (index >= NUM_DISPLAYS) {
- return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
- }
-
if (egl_init_drivers() == EGL_FALSE) {
return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
}
- EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display);
- return dpy;
+ // Call down the chain, which usually points directly to the impl
+ // but may also be routed through layers
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetDisplay(display);
}
-// ----------------------------------------------------------------------------
-// Initialization
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
-{
+EGLDisplay eglGetPlatformDisplay(EGLenum platform, EGLNativeDisplayType display,
+ const EGLAttrib* attrib_list) {
+ ATRACE_CALL();
clearError();
- egl_display_ptr dp = get_display(dpy);
- if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-
- EGLBoolean res = dp->initialize(major, minor);
-
- return res;
-}
-
-EGLBoolean eglTerminate(EGLDisplay dpy)
-{
- // NOTE: don't unload the drivers b/c some APIs can be called
- // after eglTerminate() has been called. eglTerminate() only
- // terminates an EGLDisplay, not a EGL itself.
-
- clearError();
-
- egl_display_ptr dp = get_display(dpy);
- if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-
- EGLBoolean res = dp->terminate();
-
- return res;
-}
-
-// ----------------------------------------------------------------------------
-// configuration
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglGetConfigs( EGLDisplay dpy,
- EGLConfig *configs,
- EGLint config_size, EGLint *num_config)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- if (num_config==nullptr) {
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ if (egl_init_drivers() == EGL_FALSE) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
}
- EGLBoolean res = EGL_FALSE;
- *num_config = 0;
+ // Call down the chain, which usually points directly to the impl
+ // but may also be routed through layers
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetPlatformDisplay(platform, display, attrib_list);
+}
+
+EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) {
+ clearError();
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso) {
- res = cnx->egl.eglGetConfigs(
- dp->disp.dpy, configs, config_size, num_config);
- }
-
- return res;
+ return cnx->platform.eglInitialize(dpy, major, minor);
}
-EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
- EGLConfig *configs, EGLint config_size,
- EGLint *num_config)
-{
+EGLBoolean eglTerminate(EGLDisplay dpy) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- if (num_config==nullptr) {
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
- }
-
- EGLBoolean res = EGL_FALSE;
- *num_config = 0;
-
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso) {
- if (attrib_list) {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.egl.force_msaa", value, "false");
-
- if (!strcmp(value, "true")) {
- size_t attribCount = 0;
- EGLint attrib = attrib_list[0];
-
- // Only enable MSAA if the context is OpenGL ES 2.0 and
- // if no caveat is requested
- const EGLint *attribRendererable = nullptr;
- const EGLint *attribCaveat = nullptr;
-
- // Count the number of attributes and look for
- // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
- while (attrib != EGL_NONE) {
- attrib = attrib_list[attribCount];
- switch (attrib) {
- case EGL_RENDERABLE_TYPE:
- attribRendererable = &attrib_list[attribCount];
- break;
- case EGL_CONFIG_CAVEAT:
- attribCaveat = &attrib_list[attribCount];
- break;
- default:
- break;
- }
- attribCount++;
- }
-
- if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
- (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
-
- // Insert 2 extra attributes to force-enable MSAA 4x
- EGLint aaAttribs[attribCount + 4];
- aaAttribs[0] = EGL_SAMPLE_BUFFERS;
- aaAttribs[1] = 1;
- aaAttribs[2] = EGL_SAMPLES;
- aaAttribs[3] = 4;
-
- memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
-
- EGLint numConfigAA;
- EGLBoolean resAA = cnx->egl.eglChooseConfig(
- dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
-
- if (resAA == EGL_TRUE && numConfigAA > 0) {
- ALOGD("Enabling MSAA 4x");
- *num_config = numConfigAA;
- return resAA;
- }
- }
- }
- }
-
- res = cnx->egl.eglChooseConfig(
- dp->disp.dpy, attrib_list, configs, config_size, num_config);
- }
- return res;
+ return cnx->platform.eglTerminate(dpy);
}
-EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
- EGLint attribute, EGLint *value)
-{
+EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size,
+ EGLint* num_config) {
clearError();
- egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (!dp) return EGL_FALSE;
-
- return cnx->egl.eglGetConfigAttrib(
- dp->disp.dpy, config, attribute, value);
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetConfigs(dpy, configs, config_size, num_config);
}
-// ----------------------------------------------------------------------------
-// surfaces
-// ----------------------------------------------------------------------------
-
-// Translates EGL color spaces to Android data spaces.
-static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) {
- if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
- return HAL_DATASPACE_UNKNOWN;
- } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
- return HAL_DATASPACE_SRGB;
- } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
- return HAL_DATASPACE_DISPLAY_P3;
- } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) {
- return HAL_DATASPACE_DISPLAY_P3_LINEAR;
- } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) {
- return HAL_DATASPACE_V0_SCRGB;
- } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
- return HAL_DATASPACE_V0_SCRGB_LINEAR;
- } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
- return HAL_DATASPACE_BT2020_LINEAR;
- } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
- return HAL_DATASPACE_BT2020_PQ;
- }
- return HAL_DATASPACE_UNKNOWN;
-}
-
-// Get the colorspace value that should be reported from queries. When the colorspace
-// is unknown (no attribute passed), default to reporting LINEAR.
-static EGLint getReportedColorSpace(EGLint colorspace) {
- return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace;
-}
-
-// Returns a list of color spaces understood by the vendor EGL driver.
-static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp,
- android_pixel_format format) {
- std::vector<EGLint> colorSpaces;
- if (!dp->hasColorSpaceSupport) return colorSpaces;
-
- // OpenGL drivers only support sRGB encoding with 8-bit formats.
- // RGB_888 is never returned by getNativePixelFormat, but is included for completeness.
- const bool formatSupportsSRGBEncoding =
- format == HAL_PIXEL_FORMAT_RGBA_8888 || format == HAL_PIXEL_FORMAT_RGBX_8888 ||
- format == HAL_PIXEL_FORMAT_RGB_888;
- const bool formatIsFloatingPoint = format == HAL_PIXEL_FORMAT_RGBA_FP16;
-
- if (formatSupportsSRGBEncoding) {
- // sRGB and linear are always supported when color space support is present.
- colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
- colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
- // DCI-P3 uses the sRGB transfer function, so it's only relevant for 8-bit formats.
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_display_p3")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
- }
- }
-
- // According to the spec, scRGB is only supported for floating point formats.
- // For non-linear scRGB, the application is responsible for applying the
- // transfer function.
- if (formatIsFloatingPoint) {
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_scrgb")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT);
- }
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_scrgb_linear")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
- }
- }
-
- // BT2020 can be used with any pixel format. PQ encoding must be applied by the
- // application and does not affect the behavior of OpenGL.
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_bt2020_linear")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
- }
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_bt2020_pq")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
- }
-
- // Linear DCI-P3 simply uses different primaries than standard RGB and thus
- // can be used with any pixel format.
- if (findExtension(dp->disp.queryString.extensions,
- "EGL_EXT_gl_colorspace_display_p3_linear")) {
- colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
- }
- return colorSpaces;
-}
-
-// Cleans up color space related parameters that the driver does not understand.
-// If there is no color space attribute in attrib_list, colorSpace is left
-// unmodified.
-static EGLBoolean processAttributes(egl_display_ptr dp, NativeWindowType window,
- android_pixel_format format, const EGLint* attrib_list,
- EGLint* colorSpace,
- std::vector<EGLint>* strippedAttribList) {
- for (const EGLint* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
- bool copyAttribute = true;
- if (attr[0] == EGL_GL_COLORSPACE_KHR) {
- // Fail immediately if the driver doesn't have color space support at all.
- if (!dp->hasColorSpaceSupport) return false;
- *colorSpace = attr[1];
-
- // Strip the attribute if the driver doesn't understand it.
- copyAttribute = false;
- std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp, format);
- for (auto driverColorSpace : driverColorSpaces) {
- if (attr[1] == driverColorSpace) {
- copyAttribute = true;
- break;
- }
- }
-
- // If the driver doesn't understand it, we should map sRGB-encoded P3 to
- // sRGB rather than just dropping the colorspace on the floor.
- // For this format, the driver is expected to apply the sRGB
- // transfer function during framebuffer operations.
- if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
- strippedAttribList->push_back(attr[0]);
- strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR);
- }
- }
- if (copyAttribute) {
- strippedAttribList->push_back(attr[0]);
- strippedAttribList->push_back(attr[1]);
- }
- }
- // Terminate the attribute list.
- strippedAttribList->push_back(EGL_NONE);
-
- // If the passed color space has wide color gamut, check whether the target native window
- // supports wide color.
- const bool colorSpaceIsNarrow =
- *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR ||
- *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR ||
- *colorSpace == EGL_UNKNOWN;
- if (window && !colorSpaceIsNarrow) {
- bool windowSupportsWideColor = true;
- // Ordinarily we'd put a call to native_window_get_wide_color_support
- // at the beginning of the function so that we'll have the
- // result when needed elsewhere in the function.
- // However, because eglCreateWindowSurface is called by SurfaceFlinger and
- // SurfaceFlinger is required to answer the call below we would
- // end up in a deadlock situation. By moving the call to only happen
- // if the application has specifically asked for wide-color we avoid
- // the deadlock with SurfaceFlinger since it will not ask for a
- // wide-color surface.
- int err = native_window_get_wide_color_support(window, &windowSupportsWideColor);
-
- if (err) {
- ALOGE("processAttributes: invalid window (win=%p) "
- "failed (%#x) (already connected to another API?)",
- window, err);
- return false;
- }
- if (!windowSupportsWideColor) {
- // Application has asked for a wide-color colorspace but
- // wide-color support isn't available on the display the window is on.
- return false;
- }
- }
- return true;
-}
-
-// Gets the native pixel format corrsponding to the passed EGLConfig.
-void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
- android_pixel_format* format) {
- // Set the native window's buffers format to match what this config requests.
- // Whether to use sRGB gamma is not part of the EGLconfig, but is part
- // of our native format. So if sRGB gamma is requested, we have to
- // modify the EGLconfig's format before setting the native window's
- // format.
-
- EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
- cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType);
-
- EGLint a = 0;
- EGLint r, g, b;
- r = g = b = 0;
- cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
- cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
- cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
- cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
- EGLint colorDepth = r + g + b;
-
- // Today, the driver only understands sRGB and linear on 888X
- // formats. Strip other colorspaces from the attribute list and
- // only use them to set the dataspace via
- // native_window_set_buffers_dataspace
- // if pixel format is RGBX 8888
- // TBD: Can test for future extensions that indicate that driver
- // handles requested color space and we can let it through.
- // allow SRGB and LINEAR. All others need to be stripped.
- // else if 565, 4444
- // TBD: Can we assume these are supported if 8888 is?
- // else if FP16 or 1010102
- // strip colorspace from attribs.
- // endif
- if (a == 0) {
- if (colorDepth <= 16) {
- *format = HAL_PIXEL_FORMAT_RGB_565;
- } else {
- if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
- if (colorDepth > 24) {
- *format = HAL_PIXEL_FORMAT_RGBA_1010102;
- } else {
- *format = HAL_PIXEL_FORMAT_RGBX_8888;
- }
- } else {
- *format = HAL_PIXEL_FORMAT_RGBA_FP16;
- }
- }
- } else {
- if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
- if (colorDepth > 24) {
- *format = HAL_PIXEL_FORMAT_RGBA_1010102;
- } else {
- *format = HAL_PIXEL_FORMAT_RGBA_8888;
- }
- } else {
- *format = HAL_PIXEL_FORMAT_RGBA_FP16;
- }
- }
-}
-
-EGLBoolean sendSurfaceMetadata(egl_surface_t* s) {
- android_smpte2086_metadata smpteMetadata;
- if (s->getSmpte2086Metadata(smpteMetadata)) {
- int err =
- native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata);
- s->resetSmpte2086Metadata();
- if (err != 0) {
- ALOGE("error setting native window smpte2086 metadata: %s (%d)",
- strerror(-err), err);
- return EGL_FALSE;
- }
- }
- android_cta861_3_metadata cta8613Metadata;
- if (s->getCta8613Metadata(cta8613Metadata)) {
- int err =
- native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata);
- s->resetCta8613Metadata();
- if (err != 0) {
- ALOGE("error setting native window CTS 861.3 metadata: %s (%d)",
- strerror(-err), err);
- return EGL_FALSE;
- }
- }
- return EGL_TRUE;
-}
-
-EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config,
- NativeWindowType window,
- const EGLint *attrib_list)
-{
- const EGLint *origAttribList = attrib_list;
+EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs,
+ EGLint config_size, EGLint* num_config) {
clearError();
- egl_connection_t* cnx = nullptr;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (dp) {
- if (!window) {
- return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
- }
-
- int value = 0;
- window->query(window, NATIVE_WINDOW_IS_VALID, &value);
- if (!value) {
- return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
- }
-
- // NOTE: When using Vulkan backend, the Vulkan runtime makes all the
- // native_window_* calls, so don't do them here.
- if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
- int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
- if (result < 0) {
- ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
- "failed (%#x) (already connected to another API?)",
- window, result);
- return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
- }
- }
-
- EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
- getNativePixelFormat(iDpy, cnx, config, &format);
-
- // now select correct colorspace and dataspace based on user's attribute list
- EGLint colorSpace = EGL_UNKNOWN;
- std::vector<EGLint> strippedAttribList;
- if (!processAttributes(dp, window, format, attrib_list, &colorSpace,
- &strippedAttribList)) {
- ALOGE("error invalid colorspace: %d", colorSpace);
- return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
- }
- attrib_list = strippedAttribList.data();
-
- if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
- int err = native_window_set_buffers_format(window, format);
- if (err != 0) {
- ALOGE("error setting native window pixel format: %s (%d)",
- strerror(-err), err);
- native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
- return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
- }
-
- android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace);
- if (dataSpace != HAL_DATASPACE_UNKNOWN) {
- err = native_window_set_buffers_data_space(window, dataSpace);
- if (err != 0) {
- ALOGE("error setting native window pixel dataSpace: %s (%d)", strerror(-err),
- err);
- native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
- return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
- }
- }
- }
-
- // the EGL spec requires that a new EGLSurface default to swap interval
- // 1, so explicitly set that on the window here.
- ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
- anw->setSwapInterval(anw, 1);
-
- EGLSurface surface = cnx->egl.eglCreateWindowSurface(
- iDpy, config, window, attrib_list);
- if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s =
- new egl_surface_t(dp.get(), config, window, surface,
- getReportedColorSpace(colorSpace), cnx);
- return s;
- }
-
- // EGLSurface creation failed
- if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
- native_window_set_buffers_format(window, 0);
- native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
- }
- }
- return EGL_NO_SURFACE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglChooseConfig(dpy, attrib_list, configs, config_size, num_config);
}
-EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config,
- NativePixmapType pixmap,
- const EGLint *attrib_list)
-{
+EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value) {
clearError();
- egl_connection_t* cnx = nullptr;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (dp) {
- EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
- getNativePixelFormat(iDpy, cnx, config, &format);
-
- // now select a corresponding sRGB format if needed
- EGLint colorSpace = EGL_UNKNOWN;
- std::vector<EGLint> strippedAttribList;
- if (!processAttributes(dp, nullptr, format, attrib_list, &colorSpace,
- &strippedAttribList)) {
- ALOGE("error invalid colorspace: %d", colorSpace);
- return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
- }
- attrib_list = strippedAttribList.data();
-
- EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
- dp->disp.dpy, config, pixmap, attrib_list);
- if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s =
- new egl_surface_t(dp.get(), config, nullptr, surface,
- getReportedColorSpace(colorSpace), cnx);
- return s;
- }
- }
- return EGL_NO_SURFACE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetConfigAttrib(dpy, config, attribute, value);
}
-EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
- const EGLint *attrib_list)
-{
+EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
+ const EGLint* attrib_list) {
clearError();
- egl_connection_t* cnx = nullptr;
- egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (dp) {
- EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
- getNativePixelFormat(iDpy, cnx, config, &format);
-
- // Select correct colorspace based on user's attribute list
- EGLint colorSpace = EGL_UNKNOWN;
- std::vector<EGLint> strippedAttribList;
- if (!processAttributes(dp, nullptr, format, attrib_list, &colorSpace,
- &strippedAttribList)) {
- ALOGE("error invalid colorspace: %d", colorSpace);
- return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
- }
- attrib_list = strippedAttribList.data();
-
- EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
- dp->disp.dpy, config, attrib_list);
- if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s =
- new egl_surface_t(dp.get(), config, nullptr, surface,
- getReportedColorSpace(colorSpace), cnx);
- return s;
- }
- }
- return EGL_NO_SURFACE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreateWindowSurface(dpy, config, window, attrib_list);
}
-EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface)
-{
+EGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void* native_window,
+ const EGLAttrib* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t * const s = get_surface(surface);
- EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
- if (result == EGL_TRUE) {
- _s.terminate();
- }
- return result;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreatePlatformWindowSurface(dpy, config, native_window, attrib_list);
}
-EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface,
- EGLint attribute, EGLint *value)
-{
+EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap,
+ const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreatePixmapSurface(dpy, config, pixmap, attrib_list);
+}
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+EGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap,
+ const EGLAttrib* attrib_list) {
+ clearError();
- egl_surface_t const * const s = get_surface(surface);
- if (s->getColorSpaceAttribute(attribute, value)) {
- return EGL_TRUE;
- } else if (s->getSmpte2086Attribute(attribute, value)) {
- return EGL_TRUE;
- } else if (s->getCta8613Attribute(attribute, value)) {
- return EGL_TRUE;
- }
- return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value);
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreatePlatformPixmapSurface(dpy, config, native_pixmap, attrib_list);
+}
+
+EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreatePbufferSurface(dpy, config, attrib_list);
+}
+
+EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDestroySurface(dpy, surface);
+}
+
+EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglQuerySurface(dpy, surface, attribute, value);
}
void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {
ATRACE_CALL();
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return;
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- }
+ egl_connection_t* const cnx = &gEGLImpl;
+ cnx->platform.eglBeginFrame(dpy, surface);
}
-// ----------------------------------------------------------------------------
-// Contexts
-// ----------------------------------------------------------------------------
-
-EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
- EGLContext share_list, const EGLint *attrib_list)
-{
- clearError();
-
- egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (dp) {
- if (share_list != EGL_NO_CONTEXT) {
- if (!ContextRef(dp.get(), share_list).get()) {
- return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
- }
- egl_context_t* const c = get_context(share_list);
- share_list = c->context;
- }
- EGLContext context = cnx->egl.eglCreateContext(
- dp->disp.dpy, config, share_list, attrib_list);
- if (context != EGL_NO_CONTEXT) {
- // figure out if it's a GLESv1 or GLESv2
- int version = 0;
- if (attrib_list) {
- while (*attrib_list != EGL_NONE) {
- GLint attr = *attrib_list++;
- GLint value = *attrib_list++;
- if (attr == EGL_CONTEXT_CLIENT_VERSION) {
- if (value == 1) {
- version = egl_connection_t::GLESv1_INDEX;
- } else if (value == 2 || value == 3) {
- version = egl_connection_t::GLESv2_INDEX;
- }
- }
- };
- }
- egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
- version);
- return c;
- }
- }
- return EGL_NO_CONTEXT;
-}
-
-EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp)
- return EGL_FALSE;
-
- ContextRef _c(dp.get(), ctx);
- if (!_c.get())
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
- egl_context_t * const c = get_context(ctx);
- EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
- if (result == EGL_TRUE) {
- _c.terminate();
- }
- return result;
-}
-
-EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
- EGLSurface read, EGLContext ctx)
-{
- clearError();
-
- egl_display_ptr dp = validate_display(dpy);
- if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
-
- // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
- // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
- // a valid but uninitialized display.
- if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
- (draw != EGL_NO_SURFACE) ) {
- if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
- }
-
- // get a reference to the object passed in
- ContextRef _c(dp.get(), ctx);
- SurfaceRef _d(dp.get(), draw);
- SurfaceRef _r(dp.get(), read);
-
- // validate the context (if not EGL_NO_CONTEXT)
- if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
- // EGL_NO_CONTEXT is valid
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
- }
-
- // these are the underlying implementation's object
- EGLContext impl_ctx = EGL_NO_CONTEXT;
- EGLSurface impl_draw = EGL_NO_SURFACE;
- EGLSurface impl_read = EGL_NO_SURFACE;
-
- // these are our objects structs passed in
- egl_context_t * c = nullptr;
- egl_surface_t const * d = nullptr;
- egl_surface_t const * r = nullptr;
-
- // these are the current objects structs
- egl_context_t * cur_c = get_context(getContext());
-
- if (ctx != EGL_NO_CONTEXT) {
- c = get_context(ctx);
- impl_ctx = c->context;
- } else {
- // no context given, use the implementation of the current context
- if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
- // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
- return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE);
- }
- if (cur_c == nullptr) {
- // no current context
- // not an error, there is just no current context.
- return EGL_TRUE;
- }
- }
-
- // retrieve the underlying implementation's draw EGLSurface
- if (draw != EGL_NO_SURFACE) {
- if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- d = get_surface(draw);
- impl_draw = d->surface;
- }
-
- // retrieve the underlying implementation's read EGLSurface
- if (read != EGL_NO_SURFACE) {
- if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- r = get_surface(read);
- impl_read = r->surface;
- }
-
-
- EGLBoolean result = dp->makeCurrent(c, cur_c,
- draw, read, ctx,
- impl_draw, impl_read, impl_ctx);
-
- if (result == EGL_TRUE) {
- if (c) {
- setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
- egl_tls_t::setContext(ctx);
- _c.acquire();
- _r.acquire();
- _d.acquire();
- } else {
- setGLHooksThreadSpecific(&gHooksNoContext);
- egl_tls_t::setContext(EGL_NO_CONTEXT);
- }
- } else {
- // this will ALOGE the error
- egl_connection_t* const cnx = &gEGLImpl;
- result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE);
- }
- return result;
-}
-
-
-EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
- EGLint attribute, EGLint *value)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- ContextRef _c(dp.get(), ctx);
- if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
- egl_context_t * const c = get_context(ctx);
- return c->cnx->egl.eglQueryContext(
- dp->disp.dpy, c->context, attribute, value);
-
-}
-
-EGLContext eglGetCurrentContext(void)
-{
- // could be called before eglInitialize(), but we wouldn't have a context
- // then, and this function would correctly return EGL_NO_CONTEXT.
-
- clearError();
-
- EGLContext ctx = getContext();
- return ctx;
-}
-
-EGLSurface eglGetCurrentSurface(EGLint readdraw)
-{
- // could be called before eglInitialize(), but we wouldn't have a context
- // then, and this function would correctly return EGL_NO_SURFACE.
-
- clearError();
-
- EGLContext ctx = getContext();
- if (ctx) {
- egl_context_t const * const c = get_context(ctx);
- if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
- switch (readdraw) {
- case EGL_READ: return c->read;
- case EGL_DRAW: return c->draw;
- default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
- }
- }
- return EGL_NO_SURFACE;
-}
-
-EGLDisplay eglGetCurrentDisplay(void)
-{
- // could be called before eglInitialize(), but we wouldn't have a context
- // then, and this function would correctly return EGL_NO_DISPLAY.
-
- clearError();
-
- EGLContext ctx = getContext();
- if (ctx) {
- egl_context_t const * const c = get_context(ctx);
- if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
- return c->dpy;
- }
- return EGL_NO_DISPLAY;
-}
-
-EGLBoolean eglWaitGL(void)
-{
+EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_list,
+ const EGLint* attrib_list) {
clearError();
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
- return cnx->egl.eglWaitGL();
+ return cnx->platform.eglCreateContext(dpy, config, share_list, attrib_list);
}
-EGLBoolean eglWaitNative(EGLint engine)
-{
+EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) {
clearError();
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
- return cnx->egl.eglWaitNative(engine);
+ return cnx->platform.eglDestroyContext(dpy, ctx);
}
-EGLint eglGetError(void)
-{
- EGLint err = EGL_SUCCESS;
+EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
+ clearError();
+
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso) {
- err = cnx->egl.eglGetError();
- }
- if (err == EGL_SUCCESS) {
- err = egl_tls_t::getError();
- }
- return err;
+ return cnx->platform.eglMakeCurrent(dpy, draw, read, ctx);
}
-static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
- const char* procname) {
- const egl_connection_t* cnx = &gEGLImpl;
- void* proc = nullptr;
+EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) {
+ clearError();
- proc = dlsym(cnx->libEgl, procname);
- if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
- proc = dlsym(cnx->libGles2, procname);
- if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
- proc = dlsym(cnx->libGles1, procname);
- if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
-
- return nullptr;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglQueryContext(dpy, ctx, attribute, value);
}
-__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
-{
+EGLContext eglGetCurrentContext(void) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetCurrentContext();
+}
+
+EGLSurface eglGetCurrentSurface(EGLint readdraw) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetCurrentSurface(readdraw);
+}
+
+EGLDisplay eglGetCurrentDisplay(void) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetCurrentDisplay();
+}
+
+EGLBoolean eglWaitGL(void) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglWaitGL();
+}
+
+EGLBoolean eglWaitNative(EGLint engine) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglWaitNative(engine);
+}
+
+EGLint eglGetError(void) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetError();
+}
+
+__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char* procname) {
// eglGetProcAddress() could be the very first function called
// in which case we must make sure we've initialized ourselves, this
// happens the first time egl_get_display() is called.
@@ -1203,436 +243,87 @@
if (egl_init_drivers() == EGL_FALSE) {
setError(EGL_BAD_PARAMETER, NULL);
- return nullptr;
- }
-
- if (FILTER_EXTENSIONS(procname)) {
return nullptr;
}
- __eglMustCastToProperFunctionPointerType addr;
- addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap));
- if (addr) return addr;
-
- addr = findBuiltinWrapper(procname);
- if (addr) return addr;
-
- // this protects accesses to sGLExtentionMap and sGLExtentionSlot
- pthread_mutex_lock(&sExtensionMapMutex);
-
- /*
- * Since eglGetProcAddress() is not associated to anything, it needs
- * to return a function pointer that "works" regardless of what
- * the current context is.
- *
- * For this reason, we return a "forwarder", a small stub that takes
- * care of calling the function associated with the context
- * currently bound.
- *
- * We first look for extensions we've already resolved, if we're seeing
- * this extension for the first time, we go through all our
- * implementations and call eglGetProcAddress() and record the
- * result in the appropriate implementation hooks and return the
- * address of the forwarder corresponding to that hook set.
- *
- */
-
- const std::string name(procname);
-
- auto& extentionMap = sGLExtentionMap;
- auto pos = extentionMap.find(name);
- addr = (pos != extentionMap.end()) ? pos->second : nullptr;
- const int slot = sGLExtentionSlot;
-
- ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
- "no more slots for eglGetProcAddress(\"%s\")",
- procname);
-
- if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
- bool found = false;
-
- egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglGetProcAddress) {
- // Extensions are independent of the bound context
- addr =
- cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
- cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] =
- cnx->egl.eglGetProcAddress(procname);
- if (addr) found = true;
- }
-
- if (found) {
- addr = gExtensionForwarders[slot];
- extentionMap[name] = addr;
- sGLExtentionSlot++;
- }
- }
-
- pthread_mutex_unlock(&sExtensionMapMutex);
- return addr;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetProcAddress(procname);
}
-class FrameCompletionThread {
-public:
-
- static void queueSync(EGLSyncKHR sync) {
- static FrameCompletionThread thread;
-
- char name[64];
-
- std::lock_guard<std::mutex> lock(thread.mMutex);
- snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued);
- ATRACE_NAME(name);
-
- thread.mQueue.push_back(sync);
- thread.mCondition.notify_one();
- thread.mFramesQueued++;
- ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size()));
- }
-
-private:
-
- FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
- std::thread thread(&FrameCompletionThread::loop, this);
- thread.detach();
- }
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wmissing-noreturn"
- void loop() {
- while (true) {
- threadLoop();
- }
- }
-#pragma clang diagnostic pop
-
- void threadLoop() {
- EGLSyncKHR sync;
- uint32_t frameNum;
- {
- std::unique_lock<std::mutex> lock(mMutex);
- while (mQueue.empty()) {
- mCondition.wait(lock);
- }
- sync = mQueue[0];
- frameNum = mFramesCompleted;
- }
- EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- {
- char name[64];
- snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum);
- ATRACE_NAME(name);
-
- EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
- if (result == EGL_FALSE) {
- ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
- } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
- ALOGE("FrameCompletion: timeout waiting for fence");
- }
- eglDestroySyncKHR(dpy, sync);
- }
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mQueue.pop_front();
- mFramesCompleted++;
- ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size()));
- }
- }
-
- uint32_t mFramesQueued;
- uint32_t mFramesCompleted;
- std::deque<EGLSyncKHR> mQueue;
- std::condition_variable mCondition;
- std::mutex mMutex;
-};
-
-EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw,
- EGLint *rects, EGLint n_rects)
-{
+EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+ EGLint n_rects) {
ATRACE_CALL();
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), draw);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t* const s = get_surface(draw);
-
- if (CC_UNLIKELY(dp->traceGpuCompletion)) {
- EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
- if (sync != EGL_NO_SYNC_KHR) {
- FrameCompletionThread::queueSync(sync);
- }
- }
-
- if (CC_UNLIKELY(dp->finishOnSwap)) {
- uint32_t pixel;
- egl_context_t * const c = get_context( egl_tls_t::getContext() );
- if (c) {
- // glReadPixels() ensures that the frame is complete
- s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
- GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
- }
- }
-
- if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
- if (!sendSurfaceMetadata(s)) {
- native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL);
- return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE);
- }
- }
-
- if (n_rects == 0) {
- return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
- }
-
- std::vector<android_native_rect_t> androidRects((size_t)n_rects);
- for (int r = 0; r < n_rects; ++r) {
- int offset = r * 4;
- int x = rects[offset];
- int y = rects[offset + 1];
- int width = rects[offset + 2];
- int height = rects[offset + 3];
- android_native_rect_t androidRect;
- androidRect.left = x;
- androidRect.top = y + height;
- androidRect.right = x + width;
- androidRect.bottom = y;
- androidRects.push_back(androidRect);
- }
- if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
- native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(),
- androidRects.size());
- }
-
- if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
- return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
- rects, n_rects);
- } else {
- return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
- }
-}
-
-EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
-{
- return eglSwapBuffersWithDamageKHR(dpy, surface, nullptr, 0);
-}
-
-EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface,
- NativePixmapType target)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t const * const s = get_surface(surface);
- return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
-}
-
-const char* eglQueryString(EGLDisplay dpy, EGLint name)
-{
- clearError();
-
- // Generate an error quietly when client extensions (as defined by
- // EGL_EXT_client_extensions) are queried. We do not want to rely on
- // validate_display to generate the error as validate_display would log
- // the error, which can be misleading.
- //
- // If we want to support EGL_EXT_client_extensions later, we can return
- // the client extension string here instead.
- if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS)
- return setErrorQuiet(EGL_BAD_DISPLAY, (const char*)nullptr);
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return (const char *) nullptr;
-
- switch (name) {
- case EGL_VENDOR:
- return dp->getVendorString();
- case EGL_VERSION:
- return dp->getVersionString();
- case EGL_EXTENSIONS:
- return dp->getExtensionString();
- case EGL_CLIENT_APIS:
- return dp->getClientApiString();
- default:
- break;
- }
- return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
-}
-
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return (const char *) nullptr;
-
- switch (name) {
- case EGL_VENDOR:
- return dp->disp.queryString.vendor;
- case EGL_VERSION:
- return dp->disp.queryString.version;
- case EGL_EXTENSIONS:
- return dp->disp.queryString.extensions;
- case EGL_CLIENT_APIS:
- return dp->disp.queryString.clientApi;
- default:
- break;
- }
- return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
-}
-
-// ----------------------------------------------------------------------------
-// EGL 1.1
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglSurfaceAttrib(
- EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t * const s = get_surface(surface);
-
- if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
- if (!s->getNativeWindow()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- }
- int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
- return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- if (attribute == EGL_TIMESTAMPS_ANDROID) {
- if (!s->getNativeWindow()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
- int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
- return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- if (s->setSmpte2086Attribute(attribute, value)) {
- return EGL_TRUE;
- } else if (s->setCta8613Attribute(attribute, value)) {
- return EGL_TRUE;
- } else if (s->cnx->egl.eglSurfaceAttrib) {
- return s->cnx->egl.eglSurfaceAttrib(
- dp->disp.dpy, s->surface, attribute, value);
- }
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglBindTexImage(
- EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t const * const s = get_surface(surface);
- if (s->cnx->egl.eglBindTexImage) {
- return s->cnx->egl.eglBindTexImage(
- dp->disp.dpy, s->surface, buffer);
- }
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglReleaseTexImage(
- EGLDisplay dpy, EGLSurface surface, EGLint buffer)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t const * const s = get_surface(surface);
- if (s->cnx->egl.eglReleaseTexImage) {
- return s->cnx->egl.eglReleaseTexImage(
- dp->disp.dpy, s->surface, buffer);
- }
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-}
-
-EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
-{
- clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean res = EGL_TRUE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglSwapInterval) {
- res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval);
- }
-
- return res;
+ return cnx->platform.eglSwapBuffersWithDamageKHR(dpy, draw, rects, n_rects);
}
-
-// ----------------------------------------------------------------------------
-// EGL 1.2
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglWaitClient(void)
-{
+EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
+ ATRACE_CALL();
clearError();
egl_connection_t* const cnx = &gEGLImpl;
- if (!cnx->dso)
- return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
-
- EGLBoolean res;
- if (cnx->egl.eglWaitClient) {
- res = cnx->egl.eglWaitClient();
- } else {
- res = cnx->egl.eglWaitGL();
- }
- return res;
+ return cnx->platform.eglSwapBuffers(dpy, surface);
}
-EGLBoolean eglBindAPI(EGLenum api)
-{
+EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) {
clearError();
- if (egl_init_drivers() == EGL_FALSE) {
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
- }
-
- // bind this API on all EGLs
- EGLBoolean res = EGL_TRUE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglBindAPI) {
- res = cnx->egl.eglBindAPI(api);
- }
- return res;
+ return cnx->platform.eglCopyBuffers(dpy, surface, target);
}
-EGLenum eglQueryAPI(void)
-{
+const char* eglQueryString(EGLDisplay dpy, EGLint name) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglQueryString(dpy, name);
+}
+
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglQueryStringImplementationANDROID(dpy, name);
+}
+
+EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglSurfaceAttrib(dpy, surface, attribute, value);
+}
+
+EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglBindTexImage(dpy, surface, buffer);
+}
+
+EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglReleaseTexImage(dpy, surface, buffer);
+}
+
+EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglSwapInterval(dpy, interval);
+}
+
+EGLBoolean eglWaitClient(void) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglWaitClient();
+}
+
+EGLBoolean eglBindAPI(EGLenum api) {
clearError();
if (egl_init_drivers() == EGL_FALSE) {
@@ -1640,806 +331,335 @@
}
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglQueryAPI) {
- return cnx->egl.eglQueryAPI();
- }
-
- // or, it can only be OpenGL ES
- return EGL_OPENGL_ES_API;
+ return cnx->platform.eglBindAPI(api);
}
-EGLBoolean eglReleaseThread(void)
-{
+EGLenum eglQueryAPI(void) {
+ clearError();
+
+ if (egl_init_drivers() == EGL_FALSE) {
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglQueryAPI();
+}
+
+EGLBoolean eglReleaseThread(void) {
clearError();
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglReleaseThread) {
- cnx->egl.eglReleaseThread();
- }
-
- // If there is context bound to the thread, release it
- egl_display_t::loseCurrent(get_context(getContext()));
-
- egl_tls_t::clearTLS();
- return EGL_TRUE;
+ return cnx->platform.eglReleaseThread();
}
-EGLSurface eglCreatePbufferFromClientBuffer(
- EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
- EGLConfig config, const EGLint *attrib_list)
-{
+EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
+ EGLConfig config, const EGLint* attrib_list) {
clearError();
- egl_connection_t* cnx = nullptr;
- const egl_display_ptr dp = validate_display_connection(dpy, cnx);
- if (!dp) return EGL_FALSE;
- if (cnx->egl.eglCreatePbufferFromClientBuffer) {
- return cnx->egl.eglCreatePbufferFromClientBuffer(
- dp->disp.dpy, buftype, buffer, config, attrib_list);
- }
- return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreatePbufferFromClientBuffer(dpy, buftype, buffer, config,
+ attrib_list);
}
-// ----------------------------------------------------------------------------
-// EGL_EGLEXT_VERSION 3
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface,
- const EGLint *attrib_list)
-{
+EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t const * const s = get_surface(surface);
- if (s->cnx->egl.eglLockSurfaceKHR) {
- return s->cnx->egl.eglLockSurfaceKHR(
- dp->disp.dpy, s->surface, attrib_list);
- }
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglLockSurfaceKHR(dpy, surface, attrib_list);
}
-EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface)
-{
+EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get())
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
-
- egl_surface_t const * const s = get_surface(surface);
- if (s->cnx->egl.eglUnlockSurfaceKHR) {
- return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
- }
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglUnlockSurfaceKHR(dpy, surface);
}
EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
- EGLClientBuffer buffer, const EGLint *attrib_list)
-{
+ EGLClientBuffer buffer, const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_IMAGE_KHR;
-
- ContextRef _c(dp.get(), ctx);
- egl_context_t * const c = _c.get();
-
- EGLImageKHR result = EGL_NO_IMAGE_KHR;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglCreateImageKHR) {
- result = cnx->egl.eglCreateImageKHR(
- dp->disp.dpy,
- c ? c->context : EGL_NO_CONTEXT,
- target, buffer, attrib_list);
- }
- return result;
+ return cnx->platform.eglCreateImageKHR(dpy, ctx, target, buffer, attrib_list);
}
-EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
-{
+EGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
+ const EGLAttrib* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglDestroyImageKHR) {
- result = cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img);
- }
- return result;
+ return cnx->platform.eglCreateImage(dpy, ctx, target, buffer, attrib_list);
+}
+
+EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDestroyImageKHR(dpy, img);
+}
+
+EGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImageKHR img) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDestroyImage(dpy, img);
}
// ----------------------------------------------------------------------------
// EGL_EGLEXT_VERSION 5
// ----------------------------------------------------------------------------
-
-EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
-{
+EGLSyncKHR eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_SYNC_KHR;
-
- EGLSyncKHR result = EGL_NO_SYNC_KHR;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglCreateSyncKHR) {
- result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list);
- }
- return result;
+ return cnx->platform.eglCreateSync(dpy, type, attrib_list);
}
-EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
-{
+EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglDestroySyncKHR) {
- result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync);
- }
- return result;
+ return cnx->platform.eglCreateSyncKHR(dpy, type, attrib_list);
+}
+
+EGLBoolean eglDestroySync(EGLDisplay dpy, EGLSyncKHR sync) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDestroySync(dpy, sync);
+}
+
+EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDestroySyncKHR(dpy, sync);
}
EGLBoolean eglSignalSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglSignalSyncKHR) {
- result = cnx->egl.eglSignalSyncKHR(
- dp->disp.dpy, sync, mode);
- }
- return result;
+ return cnx->platform.eglSignalSyncKHR(dpy, sync, mode);
}
-EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync,
- EGLint flags, EGLTimeKHR timeout)
-{
+EGLint eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLint result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) {
- result = cnx->egl.eglClientWaitSyncKHR(
- dp->disp.dpy, sync, flags, timeout);
- }
- return result;
+ return cnx->platform.eglClientWaitSyncKHR(dpy, sync, flags, timeout);
}
-EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync,
- EGLint attribute, EGLint *value)
-{
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) {
- result = cnx->egl.eglGetSyncAttribKHR(
- dp->disp.dpy, sync, attribute, value);
- }
- return result;
+ return cnx->platform.eglClientWaitSyncKHR(dpy, sync, flags, timeout);
}
-EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint *attrib_list)
-{
+EGLBoolean eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_STREAM_KHR;
-
- EGLStreamKHR result = EGL_NO_STREAM_KHR;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
- result = cnx->egl.eglCreateStreamKHR(
- dp->disp.dpy, attrib_list);
- }
- return result;
+ return cnx->platform.eglGetSyncAttrib(dpy, sync, attribute, value);
}
-EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream)
-{
+EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint* value) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
- result = cnx->egl.eglDestroyStreamKHR(
- dp->disp.dpy, stream);
- }
- return result;
+ return cnx->platform.eglGetSyncAttribKHR(dpy, sync, attribute, value);
}
-EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLint value)
-{
+EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
- result = cnx->egl.eglStreamAttribKHR(
- dp->disp.dpy, stream, attribute, value);
- }
- return result;
+ return cnx->platform.eglCreateStreamKHR(dpy, attrib_list);
}
-EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLint *value)
-{
+EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
- result = cnx->egl.eglQueryStreamKHR(
- dp->disp.dpy, stream, attribute, value);
- }
- return result;
+ return cnx->platform.eglDestroyStreamKHR(dpy, stream);
}
-EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLuint64KHR *value)
-{
+EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLint value) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
- result = cnx->egl.eglQueryStreamu64KHR(
- dp->disp.dpy, stream, attribute, value);
- }
- return result;
+ return cnx->platform.eglStreamAttribKHR(dpy, stream, attribute, value);
}
-EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream,
- EGLenum attribute, EGLTimeKHR *value)
-{
+EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLint* value) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
- result = cnx->egl.eglQueryStreamTimeKHR(
- dp->disp.dpy, stream, attribute, value);
- }
- return result;
+ return cnx->platform.eglQueryStreamKHR(dpy, stream, attribute, value);
}
-EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config,
- EGLStreamKHR stream, const EGLint *attrib_list)
-{
+EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLuint64KHR* value) {
clearError();
- egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_SURFACE;
-
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
- EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
- dp->disp.dpy, config, stream, attrib_list);
- if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
- EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
- return s;
- }
- }
- return EGL_NO_SURFACE;
+ return cnx->platform.eglQueryStreamu64KHR(dpy, stream, attribute, value);
}
-EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
+EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute,
+ EGLTimeKHR* value) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
- result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
- dp->disp.dpy, stream);
- }
- return result;
+ return cnx->platform.eglQueryStreamTimeKHR(dpy, stream, attribute, value);
}
-EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
+EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream,
+ const EGLint* attrib_list) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
- result = cnx->egl.eglStreamConsumerAcquireKHR(
- dp->disp.dpy, stream);
- }
- return result;
+ return cnx->platform.eglCreateStreamProducerSurfaceKHR(dpy, config, stream, attrib_list);
}
-EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy,
- EGLStreamKHR stream)
-{
+EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy, EGLStreamKHR stream) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
-
- EGLBoolean result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
- result = cnx->egl.eglStreamConsumerReleaseKHR(
- dp->disp.dpy, stream);
- }
- return result;
+ return cnx->platform.eglStreamConsumerGLTextureExternalKHR(dpy, stream);
}
-EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(
- EGLDisplay dpy, EGLStreamKHR stream)
-{
+EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy, EGLStreamKHR stream) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
-
- EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
- result = cnx->egl.eglGetStreamFileDescriptorKHR(
- dp->disp.dpy, stream);
- }
- return result;
+ return cnx->platform.eglStreamConsumerAcquireKHR(dpy, stream);
}
-EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(
- EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
-{
+EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy, EGLStreamKHR stream) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_STREAM_KHR;
-
- EGLStreamKHR result = EGL_NO_STREAM_KHR;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
- result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
- dp->disp.dpy, file_descriptor);
- }
- return result;
+ return cnx->platform.eglStreamConsumerReleaseKHR(dpy, stream);
}
-// ----------------------------------------------------------------------------
-// EGL_EGLEXT_VERSION 15
-// ----------------------------------------------------------------------------
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(EGLDisplay dpy, EGLStreamKHR stream) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetStreamFileDescriptorKHR(dpy, stream);
+}
+
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(EGLDisplay dpy,
+ EGLNativeFileDescriptorKHR file_descriptor) {
+ clearError();
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglCreateStreamFromFileDescriptorKHR(dpy, file_descriptor);
+}
EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_FALSE;
- EGLint result = EGL_FALSE;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglWaitSyncKHR) {
- result = cnx->egl.eglWaitSyncKHR(dp->disp.dpy, sync, flags);
- }
- return result;
+ return cnx->platform.eglWaitSyncKHR(dpy, sync, flags);
}
-// ----------------------------------------------------------------------------
-// ANDROID extensions
-// ----------------------------------------------------------------------------
-
-EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync)
-{
+EGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags) {
clearError();
-
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
-
- EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
egl_connection_t* const cnx = &gEGLImpl;
- if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) {
- result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync);
- }
- return result;
+ return cnx->platform.eglWaitSync(dpy, sync, flags);
}
-EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface,
- EGLnsecsANDROID time)
-{
+EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return EGL_FALSE;
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- return EGL_FALSE;
- }
-
- egl_surface_t const * const s = get_surface(surface);
- native_window_set_buffers_timestamp(s->getNativeWindow(), time);
-
- return EGL_TRUE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglDupNativeFenceFDANDROID(dpy, sync);
}
-EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) {
+EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface, EGLnsecsANDROID time) {
clearError();
- // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
- // this function cannot be implemented when this libEGL is built for
- // vendors.
-#ifndef __ANDROID_VNDK__
- if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
- return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
-#else
- return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
-#endif
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglPresentationTimeANDROID(dpy, surface, time);
}
-// ----------------------------------------------------------------------------
-// NVIDIA extensions
-// ----------------------------------------------------------------------------
-EGLuint64NV eglGetSystemTimeFrequencyNV()
-{
+EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer* buffer) {
+ clearError();
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetNativeClientBufferANDROID(buffer);
+}
+
+EGLuint64NV eglGetSystemTimeFrequencyNV() {
clearError();
if (egl_init_drivers() == EGL_FALSE) {
return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
}
- EGLuint64NV ret = 0;
egl_connection_t* const cnx = &gEGLImpl;
-
- if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) {
- return cnx->egl.eglGetSystemTimeFrequencyNV();
- }
-
- return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+ return cnx->platform.eglGetSystemTimeFrequencyNV();
}
-EGLuint64NV eglGetSystemTimeNV()
-{
+EGLuint64NV eglGetSystemTimeNV() {
clearError();
if (egl_init_drivers() == EGL_FALSE) {
return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE);
}
- EGLuint64NV ret = 0;
egl_connection_t* const cnx = &gEGLImpl;
-
- if (cnx->dso && cnx->egl.eglGetSystemTimeNV) {
- return cnx->egl.eglGetSystemTimeNV();
- }
-
- return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+ return cnx->platform.eglGetSystemTimeNV();
}
-// ----------------------------------------------------------------------------
-// Partial update extension
-// ----------------------------------------------------------------------------
-EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface,
- EGLint *rects, EGLint n_rects)
-{
+EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, EGLint* rects,
+ EGLint n_rects) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- setError(EGL_BAD_DISPLAY, EGL_FALSE);
- return EGL_FALSE;
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- setError(EGL_BAD_SURFACE, EGL_FALSE);
- return EGL_FALSE;
- }
-
- egl_surface_t const * const s = get_surface(surface);
- if (s->cnx->egl.eglSetDamageRegionKHR) {
- return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
- rects, n_rects);
- }
-
- return EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglSetDamageRegionKHR(dpy, surface, rects, n_rects);
}
-EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
- EGLuint64KHR *frameId) {
+EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR* frameId) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- egl_surface_t const * const s = get_surface(surface);
-
- if (!s->getNativeWindow()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- uint64_t nextFrameId = 0;
- int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId);
-
- if (ret != 0) {
- // This should not happen. Return an error that is not in the spec
- // so it's obvious something is very wrong.
- ALOGE("eglGetNextFrameId: Unexpected error.");
- return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
- }
-
- *frameId = nextFrameId;
- return EGL_TRUE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetNextFrameIdANDROID(dpy, surface, frameId);
}
-EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface,
- EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
-{
+EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps,
+ const EGLint* names, EGLnsecsANDROID* values) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- egl_surface_t const * const s = get_surface(surface);
-
- if (!s->getNativeWindow()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- nsecs_t* compositeDeadline = nullptr;
- nsecs_t* compositeInterval = nullptr;
- nsecs_t* compositeToPresentLatency = nullptr;
-
- for (int i = 0; i < numTimestamps; i++) {
- switch (names[i]) {
- case EGL_COMPOSITE_DEADLINE_ANDROID:
- compositeDeadline = &values[i];
- break;
- case EGL_COMPOSITE_INTERVAL_ANDROID:
- compositeInterval = &values[i];
- break;
- case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
- compositeToPresentLatency = &values[i];
- break;
- default:
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
- }
- }
-
- int ret = native_window_get_compositor_timing(s->getNativeWindow(),
- compositeDeadline, compositeInterval, compositeToPresentLatency);
-
- switch (ret) {
- case 0:
- return EGL_TRUE;
- case -ENOSYS:
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- default:
- // This should not happen. Return an error that is not in the spec
- // so it's obvious something is very wrong.
- ALOGE("eglGetCompositorTiming: Unexpected error.");
- return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
- }
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetCompositorTimingANDROID(dpy, surface, numTimestamps, names, values);
}
-EGLBoolean eglGetCompositorTimingSupportedANDROID(
- EGLDisplay dpy, EGLSurface surface, EGLint name)
-{
+EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint name) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- egl_surface_t const * const s = get_surface(surface);
-
- ANativeWindow* window = s->getNativeWindow();
- if (!window) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- switch (name) {
- case EGL_COMPOSITE_DEADLINE_ANDROID:
- case EGL_COMPOSITE_INTERVAL_ANDROID:
- case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
- return EGL_TRUE;
- default:
- return EGL_FALSE;
- }
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetCompositorTimingSupportedANDROID(dpy, surface, name);
}
-EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
- EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
- EGLnsecsANDROID *values)
-{
+EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId,
+ EGLint numTimestamps, const EGLint* timestamps,
+ EGLnsecsANDROID* values) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- egl_surface_t const * const s = get_surface(surface);
-
- if (!s->getNativeWindow()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- nsecs_t* requestedPresentTime = nullptr;
- nsecs_t* acquireTime = nullptr;
- nsecs_t* latchTime = nullptr;
- nsecs_t* firstRefreshStartTime = nullptr;
- nsecs_t* gpuCompositionDoneTime = nullptr;
- nsecs_t* lastRefreshStartTime = nullptr;
- nsecs_t* displayPresentTime = nullptr;
- nsecs_t* dequeueReadyTime = nullptr;
- nsecs_t* releaseTime = nullptr;
-
- for (int i = 0; i < numTimestamps; i++) {
- switch (timestamps[i]) {
- case EGL_REQUESTED_PRESENT_TIME_ANDROID:
- requestedPresentTime = &values[i];
- break;
- case EGL_RENDERING_COMPLETE_TIME_ANDROID:
- acquireTime = &values[i];
- break;
- case EGL_COMPOSITION_LATCH_TIME_ANDROID:
- latchTime = &values[i];
- break;
- case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
- firstRefreshStartTime = &values[i];
- break;
- case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
- lastRefreshStartTime = &values[i];
- break;
- case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
- gpuCompositionDoneTime = &values[i];
- break;
- case EGL_DISPLAY_PRESENT_TIME_ANDROID:
- displayPresentTime = &values[i];
- break;
- case EGL_DEQUEUE_READY_TIME_ANDROID:
- dequeueReadyTime = &values[i];
- break;
- case EGL_READS_DONE_TIME_ANDROID:
- releaseTime = &values[i];
- break;
- default:
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
- }
- }
-
- int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
- requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
- lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
- dequeueReadyTime, releaseTime);
-
- switch (ret) {
- case 0:
- return EGL_TRUE;
- case -ENOENT:
- return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
- case -ENOSYS:
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- case -EINVAL:
- return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
- default:
- // This should not happen. Return an error that is not in the spec
- // so it's obvious something is very wrong.
- ALOGE("eglGetFrameTimestamps: Unexpected error.");
- return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
- }
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetFrameTimestampsANDROID(dpy, surface, frameId, numTimestamps,
+ timestamps, values);
}
-EGLBoolean eglGetFrameTimestampSupportedANDROID(
- EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
-{
+EGLBoolean eglGetFrameTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface,
+ EGLint timestamp) {
clearError();
- const egl_display_ptr dp = validate_display(dpy);
- if (!dp) {
- return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
- }
-
- SurfaceRef _s(dp.get(), surface);
- if (!_s.get()) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- egl_surface_t const * const s = get_surface(surface);
-
- ANativeWindow* window = s->getNativeWindow();
- if (!window) {
- return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
- }
-
- switch (timestamp) {
- case EGL_COMPOSITE_DEADLINE_ANDROID:
- case EGL_COMPOSITE_INTERVAL_ANDROID:
- case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
- case EGL_REQUESTED_PRESENT_TIME_ANDROID:
- case EGL_RENDERING_COMPLETE_TIME_ANDROID:
- case EGL_COMPOSITION_LATCH_TIME_ANDROID:
- case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
- case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
- case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
- case EGL_DEQUEUE_READY_TIME_ANDROID:
- case EGL_READS_DONE_TIME_ANDROID:
- return EGL_TRUE;
- case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
- int value = 0;
- window->query(window,
- NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
- return value == 0 ? EGL_FALSE : EGL_TRUE;
- }
- default:
- return EGL_FALSE;
- }
+ egl_connection_t* const cnx = &gEGLImpl;
+ return cnx->platform.eglGetFrameTimestampSupportedANDROID(dpy, surface, timestamp);
}
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index d452a6c..476b304 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -46,7 +46,8 @@
// ----------------------------------------------------------------------------
static char const * const sVendorString = "Android";
-static char const * const sVersionString = "1.4 Android META-EGL";
+static char const* const sVersionString14 = "1.4 Android META-EGL";
+static char const* const sVersionString15 = "1.5 Android META-EGL";
static char const * const sClientApiString = "OpenGL_ES";
extern char const * const gBuiltinExtensionString;
@@ -119,14 +120,15 @@
return false;
}
-EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp) {
+EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp,
+ const EGLAttrib* attrib_list) {
if (uintptr_t(disp) >= NUM_DISPLAYS)
return nullptr;
- return sDisplay[uintptr_t(disp)].getDisplay(disp);
+ return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list);
}
-static void addAnglePlatformAttributes(egl_connection_t* const cnx, const EGLAttrib* attrib_list,
+static bool addAnglePlatformAttributes(egl_connection_t* const cnx, const EGLAttrib* attrib_list,
std::vector<EGLAttrib>& attrs) {
intptr_t vendorEGL = (intptr_t)cnx->vendorEGL;
@@ -153,24 +155,29 @@
}
}
- cnx->angleBackend = angleBackendDefault;
-
// Allow debug property to override application's
char prop[PROPERTY_VALUE_MAX];
property_get("debug.angle.backend", prop, "0");
switch (atoi(prop)) {
case 1:
ALOGV("addAnglePlatformAttributes: Requesting OpenGLES back-end");
- cnx->angleBackend = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
+ angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
break;
case 2:
ALOGV("addAnglePlatformAttributes: Requesting Vulkan back-end");
- cnx->angleBackend = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
+ angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
break;
default:
break;
}
+ if (cnx->angleBackend == 0) {
+ // Haven't been initialized yet, so set it.
+ cnx->angleBackend = angleBackendDefault;
+ } else if (cnx->angleBackend != angleBackendDefault) {
+ return false;
+ }
+
attrs.reserve(4 * 2);
attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
@@ -198,6 +205,8 @@
}
attrs.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE);
attrs.push_back(EGL_FALSE);
+
+ return true;
}
// Initialize function ptrs for ANGLE PlatformMethods struct, used for systrace
@@ -237,24 +246,30 @@
return true;
}
-static EGLDisplay getDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx) {
+static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx,
+ const EGLAttrib* attrib_list, EGLint* error) {
EGLDisplay dpy = EGL_NO_DISPLAY;
+ *error = EGL_NONE;
- // Locally define this until EGL 1.5 is supported
- typedef EGLDisplay (*PFNEGLGETPLATFORMDISPLAYPROC)(EGLenum platform, void* native_display,
- const EGLAttrib* attrib_list);
-
- PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplay =
- reinterpret_cast<PFNEGLGETPLATFORMDISPLAYPROC>(
- cnx->egl.eglGetProcAddress("eglGetPlatformDisplay"));
-
- if (eglGetPlatformDisplay) {
+ if (cnx->egl.eglGetPlatformDisplay) {
std::vector<EGLAttrib> attrs;
- addAnglePlatformAttributes(cnx, nullptr, attrs);
+ if (attrib_list) {
+ for (const EGLAttrib* attr = attrib_list; *attr != EGL_NONE; attr += 2) {
+ attrs.push_back(attr[0]);
+ attrs.push_back(attr[1]);
+ }
+ }
+
+ if (!addAnglePlatformAttributes(cnx, attrib_list, attrs)) {
+ ALOGE("eglGetDisplay(%p) failed: Mismatch display request", display);
+ *error = EGL_BAD_PARAMETER;
+ return EGL_NO_DISPLAY;
+ }
attrs.push_back(EGL_NONE);
- dpy = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
- reinterpret_cast<void*>(EGL_DEFAULT_DISPLAY), attrs.data());
+ dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
+ reinterpret_cast<void*>(EGL_DEFAULT_DISPLAY),
+ attrs.data());
if (dpy == EGL_NO_DISPLAY) {
ALOGE("eglGetPlatformDisplay failed!");
} else {
@@ -270,8 +285,8 @@
return dpy;
}
-EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) {
-
+EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display,
+ const EGLAttrib* attrib_list) {
std::lock_guard<std::mutex> _l(lock);
ATRACE_CALL();
@@ -283,10 +298,24 @@
EGLDisplay dpy = EGL_NO_DISPLAY;
if (cnx->useAngle) {
- dpy = getDisplayAngle(display, cnx);
+ EGLint error;
+ dpy = getPlatformDisplayAngle(display, cnx, attrib_list, &error);
+ if (error != EGL_NONE) {
+ return setError(error, dpy);
+ }
}
if (dpy == EGL_NO_DISPLAY) {
- dpy = cnx->egl.eglGetDisplay(display);
+ // NOTE: eglGetPlatformDisplay with a empty attribute list
+ // behaves the same as eglGetDisplay
+ if (cnx->egl.eglGetPlatformDisplay) {
+ dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANDROID_KHR, display,
+ attrib_list);
+ } else {
+ if (attrib_list) {
+ ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored");
+ }
+ dpy = cnx->egl.eglGetDisplay(display);
+ }
}
disp.dpy = dpy;
@@ -305,13 +334,20 @@
std::unique_lock<std::mutex> _l(refLock);
refs++;
if (refs > 1) {
- if (major != nullptr)
- *major = VERSION_MAJOR;
- if (minor != nullptr)
- *minor = VERSION_MINOR;
+ // We don't know what to report until we know what the
+ // driver supports. Make sure we are initialized before
+ // returning the version info.
while(!eglIsInitialized) {
refCond.wait(_l);
}
+ egl_connection_t* const cnx = &gEGLImpl;
+
+ // TODO: If device doesn't provide 1.4 or 1.5 then we'll be
+ // changing the behavior from the past where we always advertise
+ // version 1.4. May need to check that revision is valid
+ // before using cnx->major & cnx->minor
+ if (major != nullptr) *major = cnx->major;
+ if (minor != nullptr) *minor = cnx->minor;
return EGL_TRUE;
}
while(eglIsInitialized) {
@@ -358,7 +394,52 @@
// the query strings are per-display
mVendorString = sVendorString;
- mVersionString = sVersionString;
+ mVersionString.clear();
+ cnx->driverVersion = EGL_MAKE_VERSION(1, 4, 0);
+ if ((cnx->major == 1) && (cnx->minor == 5)) {
+ mVersionString = sVersionString15;
+ cnx->driverVersion = EGL_MAKE_VERSION(1, 5, 0);
+ } else if ((cnx->major == 1) && (cnx->minor == 4)) {
+ mVersionString = sVersionString14;
+ // Extensions needed for an EGL 1.4 implementation to be
+ // able to support EGL 1.5 functionality
+ std::vector<const char*> egl15extensions = {
+ "EGL_EXT_client_extensions",
+ // "EGL_EXT_platform_base", // implemented by EGL runtime
+ "EGL_KHR_image_base",
+ "EGL_KHR_fence_sync",
+ "EGL_KHR_wait_sync",
+ "EGL_KHR_create_context",
+ "EGL_EXT_create_context_robustness",
+ "EGL_KHR_gl_colorspace",
+ "EGL_ANDROID_native_fence_sync",
+ };
+ bool extensionsFound = true;
+ for (const auto& name : egl15extensions) {
+ extensionsFound &= findExtension(disp.queryString.extensions, name);
+ ALOGV("Extension %s: %s", name,
+ findExtension(disp.queryString.extensions, name) ? "Found" : "Missing");
+ }
+ // NOTE: From the spec:
+ // Creation of fence sync objects requires support from the bound
+ // client API, and will not succeed unless the client API satisfies:
+ // client API is OpenGL ES, and either the OpenGL ES version is 3.0
+ // or greater, or the GL_OES_EGL_sync extension is supported.
+ // We don't have a way to check the GL_EXTENSIONS string at this
+ // point in the code, assume that GL_OES_EGL_sync is supported
+ // because EGL_KHR_fence_sync is supported (as verified above).
+ if (extensionsFound) {
+ // Have everything needed to emulate EGL 1.5 so report EGL 1.5
+ // to the application.
+ mVersionString = sVersionString15;
+ cnx->major = 1;
+ cnx->minor = 5;
+ }
+ }
+ if (mVersionString.empty()) {
+ ALOGW("Unexpected driver version: %d.%d, want 1.4 or 1.5", cnx->major, cnx->minor);
+ mVersionString = sVersionString14;
+ }
mClientApiString = sClientApiString;
mExtensionString = gBuiltinExtensionString;
@@ -419,10 +500,12 @@
traceGpuCompletion = true;
}
- if (major != nullptr)
- *major = VERSION_MAJOR;
- if (minor != nullptr)
- *minor = VERSION_MINOR;
+ // TODO: If device doesn't provide 1.4 or 1.5 then we'll be
+ // changing the behavior from the past where we always advertise
+ // version 1.4. May need to check that revision is valid
+ // before using cnx->major & cnx->minor
+ if (major != nullptr) *major = cnx->major;
+ if (minor != nullptr) *minor = cnx->minor;
}
{ // scope for refLock
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index f764028..36856b7 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -49,6 +49,7 @@
class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes
static egl_display_t sDisplay[NUM_DISPLAYS];
EGLDisplay getDisplay(EGLNativeDisplayType display);
+ EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list);
void loseCurrentImpl(egl_context_t * cur_c);
public:
@@ -72,7 +73,7 @@
bool getObject(egl_object_t* object) const;
static egl_display_t* get(EGLDisplay dpy);
- static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp);
+ static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list);
EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c,
EGLSurface draw, EGLSurface read, EGLContext ctx,
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index b587a16..2921d51 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -44,6 +44,18 @@
/* EGL 1.4 */
+/* EGL 1.5 */
+EGL_ENTRY(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage)
+EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *)
+EGL_ENTRY(EGLSyncKHR, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync)
+EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSync, EGLint, EGLAttrib *)
+EGL_ENTRY(EGLBoolean, eglWaitSync, EGLDisplay, EGLSync, EGLint)
+
/* EGL_EGLEXT_VERSION 3 */
EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint *)
@@ -75,6 +87,10 @@
EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR, EGLDisplay, EGLNativeFileDescriptorKHR)
EGL_ENTRY(EGLint, eglWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint)
+/* EGL_EGLEXT_VERSION 20170627 */
+EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *)
+
/* ANDROID extensions */
EGL_ENTRY(EGLBoolean, eglSetSwapRectangleANDROID, EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint)
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
new file mode 100644
index 0000000..6900b8b
--- /dev/null
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -0,0 +1,433 @@
+/*
+ ** 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 "egl_layers.h"
+
+#include <EGL/egl.h>
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <android/dlext.h>
+#include <cutils/properties.h>
+#include <dlfcn.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <log/log.h>
+#include <nativebridge/native_bridge.h>
+#include <nativeloader/native_loader.h>
+#include <sys/prctl.h>
+
+namespace android {
+
+// GLES Layers
+//
+// - Layer discovery -
+// 1. Check for debug layer list from GraphicsEnv
+// 2. If none enabled, check system properties
+//
+// - Layer initializing -
+// TODO: ADD DETAIL ABOUT NEW INTERFACES
+// - InitializeLayer (provided by layer, called by loader)
+// - GetLayerProcAddress (provided by layer, called by loader)
+// - getNextLayerProcAddress (provided by loader, called by layer)
+//
+// 1. Walk through defs for egl and each gl version
+// 2. Call GetLayerProcAddress passing the name and the target hook entry point
+// - This tells the layer the next point in the chain it should call
+// 3. Replace the hook with the layer's entry point
+// - All entryoints will be present, anything unsupported by the driver will
+// have gl_unimplemented
+//
+// - Extension layering -
+// Not all functions are known to Android, so libEGL handles extensions.
+// They are looked up by applications using eglGetProcAddress
+// Layers can look them up with getNextLayerProcAddress
+
+const int kFuncCount = sizeof(platform_impl_t) / sizeof(char*) + sizeof(egl_t) / sizeof(char*) +
+ sizeof(gl_hooks_t) / sizeof(char*);
+
+typedef struct FunctionTable {
+ EGLFuncPointer x[kFuncCount];
+ EGLFuncPointer& operator[](int i) { return x[i]; }
+} FunctionTable;
+
+// TODO: Move these to class
+std::unordered_map<std::string, int> func_indices;
+// func_indices.reserve(kFuncCount);
+
+std::unordered_map<int, std::string> func_names;
+// func_names.reserve(kFuncCount);
+
+std::vector<FunctionTable> layer_functions;
+
+const void* getNextLayerProcAddress(void* layer_id, const char* name) {
+ // Use layer_id to find funcs for layer below current
+ // This is the same key provided in InitializeLayer
+ auto next_layer_funcs = reinterpret_cast<FunctionTable*>(layer_id);
+ EGLFuncPointer val;
+
+ if (func_indices.find(name) == func_indices.end()) {
+ // No entry for this function - it is an extension
+ // call down the GPA chain directly to the impl
+ ALOGV("getNextLayerProcAddress servicing %s", name);
+
+ // Look up which GPA we should use
+ int gpaIndex = func_indices["eglGetProcAddress"];
+ EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
+
+ ALOGV("Calling down the GPA chain (%llu) for %s", (unsigned long long)gpaNext, name);
+
+ // Call it for the requested function
+ typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
+ PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);
+
+ val = reinterpret_cast<EGLFuncPointer>(next(name));
+ ALOGV("Got back %llu for %s", (unsigned long long)val, name);
+
+ // We should store it now, but to do that, we need to move func_idx to the class so we can
+ // increment it separately
+ // TODO: Move func_idx to class and store the result of GPA
+ return reinterpret_cast<void*>(val);
+ }
+
+ // int index = func_indices[name];
+ // val = (*next_layer_funcs)[index];
+ // return reinterpret_cast<void*>(val);
+ return reinterpret_cast<void*>((*next_layer_funcs)[func_indices[name]]);
+}
+
+void SetupFuncMaps(FunctionTable& functions, char const* const* entries, EGLFuncPointer* curr,
+ int& func_idx) {
+ while (*entries) {
+ const char* name = *entries;
+
+ // Some names overlap, only fill with initial entry
+ // This does mean that some indices will not be used
+ if (func_indices.find(name) == func_indices.end()) {
+ func_names[func_idx] = name;
+ func_indices[name] = func_idx;
+ }
+
+ // Populate layer_functions once with initial value
+ // These values will arrive in priority order, starting with platform entries
+ if (functions[func_idx] == nullptr) {
+ functions[func_idx] = *curr;
+ }
+
+ entries++;
+ curr++;
+ func_idx++;
+ }
+}
+
+LayerLoader& LayerLoader::getInstance() {
+ // This function is mutex protected in egl_init_drivers_locked and eglGetProcAddressImpl
+ static LayerLoader layer_loader;
+
+ if (!layer_loader.layers_loaded_) layer_loader.LoadLayers();
+
+ return layer_loader;
+}
+
+const char kSystemLayerLibraryDir[] = "/data/local/debug/gles";
+
+std::string LayerLoader::GetDebugLayers() {
+ // Layers can be specified at the Java level in GraphicsEnvironemnt
+ // gpu_debug_layers = layer1:layer2:layerN
+ std::string debug_layers = android_getDebugLayers();
+
+ if (debug_layers.empty()) {
+ // Only check system properties if Java settings are empty
+ char prop[PROPERTY_VALUE_MAX];
+ property_get("debug.gles.layers", prop, "");
+ debug_layers = prop;
+ }
+
+ return debug_layers;
+}
+
+EGLFuncPointer LayerLoader::ApplyLayer(layer_setup_func layer_setup, const char* name,
+ EGLFuncPointer next) {
+ // Walk through our list of LayerSetup functions (they will already be in reverse order) to
+ // build up a call chain from the driver
+
+ EGLFuncPointer layer_entry = next;
+
+ layer_entry = layer_setup(name, layer_entry);
+
+ if (next != layer_entry) {
+ ALOGV("We succeeded, replacing hook (%llu) with layer entry (%llu), for %s",
+ (unsigned long long)next, (unsigned long long)layer_entry, name);
+ }
+
+ return layer_entry;
+}
+
+EGLFuncPointer LayerLoader::ApplyLayers(const char* name, EGLFuncPointer next) {
+ if (!layers_loaded_ || layer_setup_.empty()) return next;
+
+ ALOGV("ApplyLayers called for %s with next (%llu), current_layer_ (%i)", name,
+ (unsigned long long)next, current_layer_);
+
+ EGLFuncPointer val = next;
+
+ // Only ApplyLayers for layers that have been setup, not all layers yet
+ for (unsigned i = 0; i < current_layer_; i++) {
+ ALOGV("ApplyLayers: Calling ApplyLayer with i = %i for %s with next (%llu)", i, name,
+ (unsigned long long)next);
+ val = ApplyLayer(layer_setup_[i], name, val);
+ }
+
+ ALOGV("ApplyLayers returning %llu for %s", (unsigned long long)val, name);
+
+ return val;
+}
+
+void LayerLoader::LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer* curr,
+ char const* const* entries) {
+ while (*entries) {
+ char const* name = *entries;
+
+ EGLFuncPointer prev = *curr;
+
+ // Pass the existing entry point into the layer, replace the call with return value
+ *curr = ApplyLayer(layer_setup, name, *curr);
+
+ if (prev != *curr) {
+ ALOGV("LayerPlatformEntries: Replaced (%llu) with platform entry (%llu), for %s",
+ (unsigned long long)prev, (unsigned long long)*curr, name);
+ } else {
+ ALOGV("LayerPlatformEntries: No change(%llu) for %s, which means layer did not "
+ "intercept",
+ (unsigned long long)prev, name);
+ }
+
+ curr++;
+ entries++;
+ }
+}
+
+void LayerLoader::LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer* curr,
+ char const* const* entries) {
+ while (*entries) {
+ char const* name = *entries;
+ EGLFuncPointer prev = *curr;
+
+ // Only apply layers to driver entries if not handled by the platform
+ if (FindPlatformImplAddr(name) == nullptr) {
+ // Pass the existing entry point into the layer, replace the call with return value
+ *curr = ApplyLayer(layer_setup, name, *prev);
+
+ if (prev != *curr) {
+ ALOGV("LayerDriverEntries: Replaced (%llu) with platform entry (%llu), for %s",
+ (unsigned long long)prev, (unsigned long long)*curr, name);
+ }
+
+ } else {
+ ALOGV("LayerDriverEntries: Skipped (%llu) for %s", (unsigned long long)prev, name);
+ }
+
+ curr++;
+ entries++;
+ }
+}
+
+bool LayerLoader::Initialized() {
+ return initialized_;
+}
+
+void LayerLoader::InitLayers(egl_connection_t* cnx) {
+ if (!layers_loaded_) return;
+
+ if (initialized_) return;
+
+ if (layer_setup_.empty()) {
+ initialized_ = true;
+ return;
+ }
+
+ // Include the driver in layer_functions
+ layer_functions.resize(layer_setup_.size() + 1);
+
+ // Walk through the initial lists and create layer_functions[0]
+ int func_idx = 0;
+ char const* const* entries;
+ EGLFuncPointer* curr;
+
+ entries = platform_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
+ SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+ ALOGV("InitLayers: func_idx after platform_names: %i", func_idx);
+
+ entries = egl_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
+ SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+ ALOGV("InitLayers: func_idx after egl_names: %i", func_idx);
+
+ entries = gl_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
+ SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
+ ALOGV("InitLayers: func_idx after gl_names: %i", func_idx);
+
+ // Walk through each layer's entry points per API, starting just above the driver
+ for (current_layer_ = 0; current_layer_ < layer_setup_.size(); current_layer_++) {
+ // Init the layer with a key that points to layer just below it
+ layer_init_[current_layer_](reinterpret_cast<void*>(&layer_functions[current_layer_]),
+ reinterpret_cast<PFNEGLGETNEXTLAYERPROCADDRESSPROC>(
+ getNextLayerProcAddress));
+
+ // Check functions implemented by the platform
+ func_idx = 0;
+ entries = platform_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
+ LayerPlatformEntries(layer_setup_[current_layer_], curr, entries);
+
+ // Populate next function table after layers have been applied
+ SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+
+ // EGL
+ entries = egl_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
+ LayerDriverEntries(layer_setup_[current_layer_], curr, entries);
+
+ // Populate next function table after layers have been applied
+ SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+
+ // GLES 2+
+ // NOTE: We route calls to GLESv2 hooks, not GLESv1, so layering does not support GLES 1.x
+ // If it were added in the future, a different layer initialization model would be needed,
+ // that defers loading GLES entrypoints until after eglMakeCurrent, so two phase
+ // initialization.
+ entries = gl_names;
+ curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
+ LayerDriverEntries(layer_setup_[current_layer_], curr, entries);
+
+ // Populate next function table after layers have been applied
+ SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
+ }
+
+ // We only want to apply layers once
+ initialized_ = true;
+}
+
+void LayerLoader::LoadLayers() {
+ std::string debug_layers = GetDebugLayers();
+
+ // If no layers are specified, we're done
+ if (debug_layers.empty()) return;
+
+ // Only enable the system search path for non-user builds
+ std::string system_path;
+ if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+ system_path = kSystemLayerLibraryDir;
+ }
+
+ ALOGI("Debug layer list: %s", debug_layers.c_str());
+ std::vector<std::string> layers = android::base::Split(debug_layers, ":");
+
+ // Load the layers in reverse order so we start with the driver's entrypoint and work our way up
+ for (int32_t i = layers.size() - 1; i >= 0; i--) {
+ // Check each layer path for the layer
+ std::vector<std::string> paths = android::base::Split(android_getLayerPaths(), ":");
+
+ if (!system_path.empty()) {
+ // Prepend the system paths so they override other layers
+ auto it = paths.begin();
+ paths.insert(it, system_path);
+ }
+
+ bool layer_found = false;
+ for (uint32_t j = 0; j < paths.size() && !layer_found; j++) {
+ std::string layer;
+
+ ALOGI("Searching %s for GLES layers", paths[j].c_str());
+
+ // Realpath will return null for non-existent files
+ android::base::Realpath(paths[j] + "/" + layers[i], &layer);
+
+ if (!layer.empty()) {
+ layer_found = true;
+ ALOGI("GLES layer found: %s", layer.c_str());
+
+ // Load the layer
+ //
+ // TODO: This code is common with Vulkan loader, refactor
+ //
+ // Libraries in the system layer library dir can't be loaded into
+ // the application namespace. That causes compatibility problems, since
+ // any symbol dependencies will be resolved by system libraries. They
+ // can't safely use libc++_shared, for example. Which is one reason
+ // (among several) we only allow them in non-user builds.
+ void* handle = nullptr;
+ auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
+ if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
+ bool native_bridge = false;
+ std::string error_message;
+ handle = OpenNativeLibrary(app_namespace, layer.c_str(), &native_bridge,
+ &error_message);
+ if (!handle) {
+ ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
+ error_message.c_str());
+ return;
+ }
+
+ } else {
+ handle = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL);
+ }
+
+ if (handle) {
+ ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)handle,
+ layers[i].c_str());
+ } else {
+ // If the layer is found but can't be loaded, try setenforce 0
+ const char* dlsym_error = dlerror();
+ ALOGE("Failed to load layer %s with error: %s", layer.c_str(), dlsym_error);
+ return;
+ }
+
+ // Find the layer's Initialize function
+ std::string init_func = "InitializeLayer";
+ ALOGV("Looking for entrypoint %s", init_func.c_str());
+
+ layer_init_func LayerInit =
+ reinterpret_cast<layer_init_func>(dlsym(handle, init_func.c_str()));
+ if (LayerInit) {
+ ALOGV("Found %s for layer %s", init_func.c_str(), layer.c_str());
+ layer_init_.push_back(LayerInit);
+ } else {
+ ALOGE("Failed to dlsym %s for layer %s", init_func.c_str(), layer.c_str());
+ return;
+ }
+
+ // Find the layer's setup function
+ std::string setup_func = "GetLayerProcAddress";
+ ALOGV("Looking for entrypoint %s", setup_func.c_str());
+
+ layer_setup_func LayerSetup =
+ reinterpret_cast<layer_setup_func>(dlsym(handle, setup_func.c_str()));
+ if (LayerSetup) {
+ ALOGV("Found %s for layer %s", setup_func.c_str(), layer.c_str());
+ layer_setup_.push_back(LayerSetup);
+ } else {
+ ALOGE("Failed to dlsym %s for layer %s", setup_func.c_str(), layer.c_str());
+ return;
+ }
+ }
+ }
+ }
+ // Track this so we only attempt to load these once
+ layers_loaded_ = true;
+}
+
+} // namespace android
diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h
new file mode 100644
index 0000000..e401b44
--- /dev/null
+++ b/opengl/libs/EGL/egl_layers.h
@@ -0,0 +1,65 @@
+/*
+ * 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_EGL_LAYERS_H
+#define ANDROID_EGL_LAYERS_H
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <EGL/egldefs.h>
+
+#include "egl_platform_entries.h"
+
+typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
+
+namespace android {
+
+class LayerLoader {
+public:
+ static LayerLoader& getInstance();
+ ~LayerLoader(){};
+
+ typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*);
+ typedef EGLFuncPointer (*layer_init_func)(
+ const void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address);
+ typedef EGLFuncPointer (*layer_setup_func)(const char* name, EGLFuncPointer next);
+
+ void LoadLayers();
+ void InitLayers(egl_connection_t*);
+ void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+ void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*);
+ bool Initialized();
+ std::string GetDebugLayers();
+
+ EGLFuncPointer GetGpaNext(unsigned i);
+ EGLFuncPointer ApplyLayer(layer_setup_func layer_setup, const char* name, EGLFuncPointer next);
+ EGLFuncPointer ApplyLayers(const char* name, EGLFuncPointer next);
+
+ std::vector<layer_init_func> layer_init_;
+ std::vector<layer_setup_func> layer_setup_;
+
+private:
+ LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0){};
+ bool layers_loaded_;
+ bool initialized_;
+ unsigned current_layer_;
+};
+
+}; // namespace android
+
+#endif // ANDROID_EGL_LAYERS_H
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
new file mode 100644
index 0000000..1daa4d2
--- /dev/null
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -0,0 +1,2717 @@
+/*
+ ** Copyright 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "egl_platform_entries.h"
+
+#include <ctype.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hardware/gralloc1.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <EGL/eglext_angle.h>
+
+#include <android/hardware_buffer.h>
+#include <android-base/strings.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <private/android/AHardwareBufferHelpers.h>
+
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <log/log.h>
+
+#include <condition_variable>
+#include <deque>
+#include <mutex>
+#include <unordered_map>
+#include <string>
+#include <thread>
+
+#include "../egl_impl.h"
+
+#include "egl_display.h"
+#include "egl_object.h"
+#include "egl_layers.h"
+#include "egl_tls.h"
+#include "egl_trace.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+using nsecs_t = int64_t;
+
+struct extention_map_t {
+ const char* name;
+ __eglMustCastToProperFunctionPointerType address;
+};
+
+/*
+ * This is the list of EGL extensions exposed to applications.
+ *
+ * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL
+ * wrapper and are always available.
+ *
+ * The rest (gExtensionString) depend on support in the EGL driver, and are
+ * only available if the driver supports them. However, some of these must be
+ * supported because they are used by the Android system itself; these are
+ * listed as mandatory below and are required by the CDD. The system *assumes*
+ * the mandatory extensions are present and may not function properly if some
+ * are missing.
+ *
+ * NOTE: Both strings MUST have a single space as the last character.
+ */
+
+extern char const * const gBuiltinExtensionString;
+extern char const * const gExtensionString;
+
+// clang-format off
+// Extensions implemented by the EGL wrapper.
+char const * const gBuiltinExtensionString =
+ "EGL_KHR_get_all_proc_addresses "
+ "EGL_ANDROID_presentation_time "
+ "EGL_KHR_swap_buffers_with_damage "
+ "EGL_ANDROID_get_native_client_buffer "
+ "EGL_ANDROID_front_buffer_auto_refresh "
+ "EGL_ANDROID_get_frame_timestamps "
+ "EGL_EXT_surface_SMPTE2086_metadata "
+ "EGL_EXT_surface_CTA861_3_metadata "
+ ;
+
+// Whitelist of extensions exposed to applications if implemented in the vendor driver.
+char const * const gExtensionString =
+ "EGL_KHR_image " // mandatory
+ "EGL_KHR_image_base " // mandatory
+ "EGL_EXT_image_gl_colorspace "
+ "EGL_KHR_image_pixmap "
+ "EGL_KHR_lock_surface "
+ "EGL_KHR_gl_colorspace "
+ "EGL_KHR_gl_texture_2D_image "
+ "EGL_KHR_gl_texture_3D_image "
+ "EGL_KHR_gl_texture_cubemap_image "
+ "EGL_KHR_gl_renderbuffer_image "
+ "EGL_KHR_reusable_sync "
+ "EGL_KHR_fence_sync "
+ "EGL_KHR_create_context "
+ "EGL_KHR_config_attribs "
+ "EGL_KHR_surfaceless_context "
+ "EGL_KHR_stream "
+ "EGL_KHR_stream_fifo "
+ "EGL_KHR_stream_producer_eglsurface "
+ "EGL_KHR_stream_consumer_gltexture "
+ "EGL_KHR_stream_cross_process_fd "
+ "EGL_EXT_create_context_robustness "
+ "EGL_NV_system_time "
+ "EGL_ANDROID_image_native_buffer " // mandatory
+ "EGL_KHR_wait_sync " // strongly recommended
+ "EGL_ANDROID_recordable " // mandatory
+ "EGL_KHR_partial_update " // strongly recommended
+ "EGL_EXT_pixel_format_float "
+ "EGL_EXT_buffer_age " // strongly recommended with partial_update
+ "EGL_KHR_create_context_no_error "
+ "EGL_KHR_mutable_render_buffer "
+ "EGL_EXT_yuv_surface "
+ "EGL_EXT_protected_content "
+ "EGL_IMG_context_priority "
+ "EGL_KHR_no_config_context "
+ ;
+
+char const * const gClientExtensionString =
+ "EGL_EXT_client_extensions "
+ "EGL_KHR_platform_android "
+ "EGL_ANGLE_platform_angle";
+// clang-format on
+
+// extensions not exposed to applications but used by the ANDROID system
+// "EGL_ANDROID_blob_cache " // strongly recommended
+// "EGL_IMG_hibernate_process " // optional
+// "EGL_ANDROID_native_fence_sync " // strongly recommended
+// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1
+
+/*
+ * EGL Extensions entry-points exposed to 3rd party applications
+ * (keep in sync with gExtensionString above)
+ *
+ */
+static const extention_map_t sExtensionMap[] = {
+ // EGL_KHR_lock_surface
+ { "eglLockSurfaceKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
+ { "eglUnlockSurfaceKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
+
+ // EGL_KHR_image, EGL_KHR_image_base
+ { "eglCreateImageKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
+ { "eglDestroyImageKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
+
+ // EGL_KHR_reusable_sync, EGL_KHR_fence_sync
+ { "eglCreateSyncKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
+ { "eglDestroySyncKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
+ { "eglClientWaitSyncKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
+ { "eglSignalSyncKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR },
+ { "eglGetSyncAttribKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
+
+ // EGL_NV_system_time
+ { "eglGetSystemTimeFrequencyNV",
+ (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
+ { "eglGetSystemTimeNV",
+ (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
+
+ // EGL_KHR_wait_sync
+ { "eglWaitSyncKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR },
+
+ // EGL_ANDROID_presentation_time
+ { "eglPresentationTimeANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
+
+ // EGL_KHR_swap_buffers_with_damage
+ { "eglSwapBuffersWithDamageKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
+
+ // EGL_ANDROID_get_native_client_buffer
+ { "eglGetNativeClientBufferANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID },
+
+ // EGL_KHR_partial_update
+ { "eglSetDamageRegionKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
+
+ { "eglCreateStreamKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR },
+ { "eglDestroyStreamKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR },
+ { "eglStreamAttribKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR },
+ { "eglQueryStreamKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR },
+ { "eglQueryStreamu64KHR",
+ (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR },
+ { "eglQueryStreamTimeKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR },
+ { "eglCreateStreamProducerSurfaceKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR },
+ { "eglStreamConsumerGLTextureExternalKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR },
+ { "eglStreamConsumerAcquireKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR },
+ { "eglStreamConsumerReleaseKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR },
+ { "eglGetStreamFileDescriptorKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
+ { "eglCreateStreamFromFileDescriptorKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+
+ // EGL_ANDROID_get_frame_timestamps
+ { "eglGetNextFrameIdANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
+ { "eglGetCompositorTimingANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID },
+ { "eglGetCompositorTimingSupportedANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID },
+ { "eglGetFrameTimestampsANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+ { "eglGetFrameTimestampSupportedANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID },
+
+ // EGL_ANDROID_native_fence_sync
+ { "eglDupNativeFenceFDANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID },
+};
+
+/*
+ * These extensions entry-points should not be exposed to applications.
+ * They're used internally by the Android EGL layer.
+ */
+#define FILTER_EXTENSIONS(procname) \
+ (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \
+ !strcmp((procname), "eglHibernateProcessIMG") || \
+ !strcmp((procname), "eglAwakenProcessIMG"))
+
+// accesses protected by sExtensionMapMutex
+static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
+
+static int sGLExtentionSlot = 0;
+static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void(*findProcAddress(const char* name,
+ const extention_map_t* map, size_t n))() {
+ for (uint32_t i=0 ; i<n ; i++) {
+ if (!strcmp(name, map[i].name)) {
+ return map[i].address;
+ }
+ }
+ return nullptr;
+}
+
+// ----------------------------------------------------------------------------
+
+extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
+extern EGLBoolean egl_init_drivers();
+extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
+extern gl_hooks_t gHooksTrace;
+
+// ----------------------------------------------------------------------------
+
+static inline EGLContext getContext() { return egl_tls_t::getContext(); }
+
+// ----------------------------------------------------------------------------
+
+static EGLDisplay eglGetPlatformDisplayTmpl(EGLenum platform, EGLNativeDisplayType display,
+ const EGLAttrib* attrib_list) {
+ if (platform != EGL_PLATFORM_ANDROID_KHR) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
+ }
+
+ uintptr_t index = reinterpret_cast<uintptr_t>(display);
+ if (index >= NUM_DISPLAYS) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
+ }
+
+ EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display, attrib_list);
+ return dpy;
+}
+
+EGLDisplay eglGetDisplayImpl(EGLNativeDisplayType display) {
+ return eglGetPlatformDisplayTmpl(EGL_PLATFORM_ANDROID_KHR, display, nullptr);
+}
+
+EGLDisplay eglGetPlatformDisplayImpl(EGLenum platform, void* native_display,
+ const EGLAttrib* attrib_list) {
+ return eglGetPlatformDisplayTmpl(platform, static_cast<EGLNativeDisplayType>(native_display),
+ attrib_list);
+}
+
+// ----------------------------------------------------------------------------
+// Initialization
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor)
+{
+ egl_display_ptr dp = get_display(dpy);
+ if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+ EGLBoolean res = dp->initialize(major, minor);
+
+ return res;
+}
+
+EGLBoolean eglTerminateImpl(EGLDisplay dpy)
+{
+ // NOTE: don't unload the drivers b/c some APIs can be called
+ // after eglTerminate() has been called. eglTerminate() only
+ // terminates an EGLDisplay, not a EGL itself.
+
+ egl_display_ptr dp = get_display(dpy);
+ if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+ EGLBoolean res = dp->terminate();
+
+ return res;
+}
+
+// ----------------------------------------------------------------------------
+// configuration
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglGetConfigsImpl(EGLDisplay dpy,
+ EGLConfig *configs,
+ EGLint config_size, EGLint *num_config)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ if (num_config==nullptr) {
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ }
+
+ EGLBoolean res = EGL_FALSE;
+ *num_config = 0;
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso) {
+ res = cnx->egl.eglGetConfigs(
+ dp->disp.dpy, configs, config_size, num_config);
+ }
+
+ return res;
+}
+
+EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list,
+ EGLConfig *configs, EGLint config_size,
+ EGLint *num_config)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ if (num_config==nullptr) {
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ }
+
+ EGLBoolean res = EGL_FALSE;
+ *num_config = 0;
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso) {
+ if (attrib_list) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.egl.force_msaa", value, "false");
+
+ if (!strcmp(value, "true")) {
+ size_t attribCount = 0;
+ EGLint attrib = attrib_list[0];
+
+ // Only enable MSAA if the context is OpenGL ES 2.0 and
+ // if no caveat is requested
+ const EGLint *attribRendererable = nullptr;
+ const EGLint *attribCaveat = nullptr;
+
+ // Count the number of attributes and look for
+ // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
+ while (attrib != EGL_NONE) {
+ attrib = attrib_list[attribCount];
+ switch (attrib) {
+ case EGL_RENDERABLE_TYPE:
+ attribRendererable = &attrib_list[attribCount];
+ break;
+ case EGL_CONFIG_CAVEAT:
+ attribCaveat = &attrib_list[attribCount];
+ break;
+ default:
+ break;
+ }
+ attribCount++;
+ }
+
+ if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
+ (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
+
+ // Insert 2 extra attributes to force-enable MSAA 4x
+ EGLint aaAttribs[attribCount + 4];
+ aaAttribs[0] = EGL_SAMPLE_BUFFERS;
+ aaAttribs[1] = 1;
+ aaAttribs[2] = EGL_SAMPLES;
+ aaAttribs[3] = 4;
+
+ memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
+
+ EGLint numConfigAA;
+ EGLBoolean resAA = cnx->egl.eglChooseConfig(
+ dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
+
+ if (resAA == EGL_TRUE && numConfigAA > 0) {
+ ALOGD("Enabling MSAA 4x");
+ *num_config = numConfigAA;
+ return resAA;
+ }
+ }
+ }
+ }
+
+ res = cnx->egl.eglChooseConfig(
+ dp->disp.dpy, attrib_list, configs, config_size, num_config);
+ }
+ return res;
+}
+
+EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config,
+ EGLint attribute, EGLint *value)
+{
+ egl_connection_t* cnx = nullptr;
+ const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (!dp) return EGL_FALSE;
+
+ return cnx->egl.eglGetConfigAttrib(
+ dp->disp.dpy, config, attribute, value);
+}
+
+// ----------------------------------------------------------------------------
+// surfaces
+// ----------------------------------------------------------------------------
+
+// Translates EGL color spaces to Android data spaces.
+static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) {
+ if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) {
+ return HAL_DATASPACE_UNKNOWN;
+ } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
+ return HAL_DATASPACE_SRGB;
+ } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
+ return HAL_DATASPACE_DISPLAY_P3;
+ } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) {
+ return HAL_DATASPACE_DISPLAY_P3_LINEAR;
+ } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) {
+ return HAL_DATASPACE_V0_SCRGB;
+ } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
+ return HAL_DATASPACE_V0_SCRGB_LINEAR;
+ } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
+ return HAL_DATASPACE_BT2020_LINEAR;
+ } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
+ return HAL_DATASPACE_BT2020_PQ;
+ }
+ return HAL_DATASPACE_UNKNOWN;
+}
+
+// Get the colorspace value that should be reported from queries. When the colorspace
+// is unknown (no attribute passed), default to reporting LINEAR.
+static EGLint getReportedColorSpace(EGLint colorspace) {
+ return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace;
+}
+
+// Returns a list of color spaces understood by the vendor EGL driver.
+static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp,
+ android_pixel_format format) {
+ std::vector<EGLint> colorSpaces;
+ if (!dp->hasColorSpaceSupport) return colorSpaces;
+
+ // OpenGL drivers only support sRGB encoding with 8-bit formats.
+ // RGB_888 is never returned by getNativePixelFormat, but is included for completeness.
+ const bool formatSupportsSRGBEncoding =
+ format == HAL_PIXEL_FORMAT_RGBA_8888 || format == HAL_PIXEL_FORMAT_RGBX_8888 ||
+ format == HAL_PIXEL_FORMAT_RGB_888;
+ const bool formatIsFloatingPoint = format == HAL_PIXEL_FORMAT_RGBA_FP16;
+
+ if (formatSupportsSRGBEncoding) {
+ // sRGB and linear are always supported when color space support is present.
+ colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+ colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
+ // DCI-P3 uses the sRGB transfer function, so it's only relevant for 8-bit formats.
+ if (findExtension(dp->disp.queryString.extensions,
+ "EGL_EXT_gl_colorspace_display_p3")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT);
+ }
+ }
+
+ // According to the spec, scRGB is only supported for floating point formats.
+ // For non-linear scRGB, the application is responsible for applying the
+ // transfer function.
+ if (formatIsFloatingPoint) {
+ if (findExtension(dp->disp.queryString.extensions,
+ "EGL_EXT_gl_colorspace_scrgb")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT);
+ }
+ if (findExtension(dp->disp.queryString.extensions,
+ "EGL_EXT_gl_colorspace_scrgb_linear")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT);
+ }
+ }
+
+ // BT2020 can be used with any pixel format. PQ encoding must be applied by the
+ // application and does not affect the behavior of OpenGL.
+ if (findExtension(dp->disp.queryString.extensions,
+ "EGL_EXT_gl_colorspace_bt2020_linear")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT);
+ }
+ if (findExtension(dp->disp.queryString.extensions,
+ "EGL_EXT_gl_colorspace_bt2020_pq")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
+ }
+
+ // Linear DCI-P3 simply uses different primaries than standard RGB and thus
+ // can be used with any pixel format.
+ if (findExtension(dp->disp.queryString.extensions,
+ "EGL_EXT_gl_colorspace_display_p3_linear")) {
+ colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
+ }
+ return colorSpaces;
+}
+
+// Cleans up color space related parameters that the driver does not understand.
+// If there is no color space attribute in attrib_list, colorSpace is left
+// unmodified.
+template <typename AttrType>
+static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window,
+ android_pixel_format format, const AttrType* attrib_list,
+ EGLint* colorSpace, std::vector<AttrType>* strippedAttribList) {
+ for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
+ bool copyAttribute = true;
+ if (attr[0] == EGL_GL_COLORSPACE_KHR) {
+ // Fail immediately if the driver doesn't have color space support at all.
+ if (!dp->hasColorSpaceSupport) return false;
+ *colorSpace = static_cast<EGLint>(attr[1]);
+
+ // Strip the attribute if the driver doesn't understand it.
+ copyAttribute = false;
+ std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp, format);
+ for (auto driverColorSpace : driverColorSpaces) {
+ if (static_cast<EGLint>(attr[1]) == driverColorSpace) {
+ copyAttribute = true;
+ break;
+ }
+ }
+
+ // If the driver doesn't understand it, we should map sRGB-encoded P3 to
+ // sRGB rather than just dropping the colorspace on the floor.
+ // For this format, the driver is expected to apply the sRGB
+ // transfer function during framebuffer operations.
+ if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
+ strippedAttribList->push_back(attr[0]);
+ strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+ }
+ }
+ if (copyAttribute) {
+ strippedAttribList->push_back(attr[0]);
+ strippedAttribList->push_back(attr[1]);
+ }
+ }
+ // Terminate the attribute list.
+ strippedAttribList->push_back(EGL_NONE);
+
+ // If the passed color space has wide color gamut, check whether the target native window
+ // supports wide color.
+ const bool colorSpaceIsNarrow =
+ *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR ||
+ *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR ||
+ *colorSpace == EGL_UNKNOWN;
+ if (window && !colorSpaceIsNarrow) {
+ bool windowSupportsWideColor = true;
+ // Ordinarily we'd put a call to native_window_get_wide_color_support
+ // at the beginning of the function so that we'll have the
+ // result when needed elsewhere in the function.
+ // However, because eglCreateWindowSurface is called by SurfaceFlinger and
+ // SurfaceFlinger is required to answer the call below we would
+ // end up in a deadlock situation. By moving the call to only happen
+ // if the application has specifically asked for wide-color we avoid
+ // the deadlock with SurfaceFlinger since it will not ask for a
+ // wide-color surface.
+ int err = native_window_get_wide_color_support(window, &windowSupportsWideColor);
+
+ if (err) {
+ ALOGE("processAttributes: invalid window (win=%p) "
+ "failed (%#x) (already connected to another API?)",
+ window, err);
+ return false;
+ }
+ if (!windowSupportsWideColor) {
+ // Application has asked for a wide-color colorspace but
+ // wide-color support isn't available on the display the window is on.
+ return false;
+ }
+ }
+ return true;
+}
+
+// Note: This only works for existing GLenum's that are all 32bits.
+// If you have 64bit attributes (e.g. pointers) you shouldn't be calling this.
+void convertAttribs(const EGLAttrib* attribList, std::vector<EGLint>& newList) {
+ for (const EGLAttrib* attr = attribList; attr && attr[0] != EGL_NONE; attr += 2) {
+ newList.push_back(static_cast<EGLint>(attr[0]));
+ newList.push_back(static_cast<EGLint>(attr[1]));
+ }
+ newList.push_back(EGL_NONE);
+}
+
+// Gets the native pixel format corrsponding to the passed EGLConfig.
+void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
+ android_pixel_format* format) {
+ // Set the native window's buffers format to match what this config requests.
+ // Whether to use sRGB gamma is not part of the EGLconfig, but is part
+ // of our native format. So if sRGB gamma is requested, we have to
+ // modify the EGLconfig's format before setting the native window's
+ // format.
+
+ EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
+ cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType);
+
+ EGLint a = 0;
+ EGLint r, g, b;
+ r = g = b = 0;
+ cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
+ cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
+ cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
+ cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
+ EGLint colorDepth = r + g + b;
+
+ // Today, the driver only understands sRGB and linear on 888X
+ // formats. Strip other colorspaces from the attribute list and
+ // only use them to set the dataspace via
+ // native_window_set_buffers_dataspace
+ // if pixel format is RGBX 8888
+ // TBD: Can test for future extensions that indicate that driver
+ // handles requested color space and we can let it through.
+ // allow SRGB and LINEAR. All others need to be stripped.
+ // else if 565, 4444
+ // TBD: Can we assume these are supported if 8888 is?
+ // else if FP16 or 1010102
+ // strip colorspace from attribs.
+ // endif
+ if (a == 0) {
+ if (colorDepth <= 16) {
+ *format = HAL_PIXEL_FORMAT_RGB_565;
+ } else {
+ if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+ if (colorDepth > 24) {
+ *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+ } else {
+ *format = HAL_PIXEL_FORMAT_RGBX_8888;
+ }
+ } else {
+ *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+ }
+ }
+ } else {
+ if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
+ if (colorDepth > 24) {
+ *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+ } else {
+ *format = HAL_PIXEL_FORMAT_RGBA_8888;
+ }
+ } else {
+ *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+ }
+ }
+}
+
+EGLBoolean sendSurfaceMetadata(egl_surface_t* s) {
+ android_smpte2086_metadata smpteMetadata;
+ if (s->getSmpte2086Metadata(smpteMetadata)) {
+ int err =
+ native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata);
+ s->resetSmpte2086Metadata();
+ if (err != 0) {
+ ALOGE("error setting native window smpte2086 metadata: %s (%d)",
+ strerror(-err), err);
+ return EGL_FALSE;
+ }
+ }
+ android_cta861_3_metadata cta8613Metadata;
+ if (s->getCta8613Metadata(cta8613Metadata)) {
+ int err =
+ native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata);
+ s->resetCta8613Metadata();
+ if (err != 0) {
+ ALOGE("error setting native window CTS 861.3 metadata: %s (%d)",
+ strerror(-err), err);
+ return EGL_FALSE;
+ }
+ }
+ return EGL_TRUE;
+}
+
+template <typename AttrType, typename CreateFuncType>
+EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config,
+ ANativeWindow* window, const AttrType* attrib_list,
+ CreateFuncType createWindowSurfaceFunc) {
+ const AttrType* origAttribList = attrib_list;
+
+ if (!window) {
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
+
+ int value = 0;
+ window->query(window, NATIVE_WINDOW_IS_VALID, &value);
+ if (!value) {
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
+
+ // NOTE: When using Vulkan backend, the Vulkan runtime makes all the
+ // native_window_* calls, so don't do them here.
+ if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+ int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+ if (result < 0) {
+ ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
+ "failed (%#x) (already connected to another API?)",
+ window, result);
+ return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
+ }
+ }
+
+ EGLDisplay iDpy = dp->disp.dpy;
+ android_pixel_format format;
+ getNativePixelFormat(iDpy, cnx, config, &format);
+
+ // now select correct colorspace and dataspace based on user's attribute list
+ EGLint colorSpace = EGL_UNKNOWN;
+ std::vector<AttrType> strippedAttribList;
+ if (!processAttributes<AttrType>(dp, window, format, attrib_list, &colorSpace,
+ &strippedAttribList)) {
+ ALOGE("error invalid colorspace: %d", colorSpace);
+ return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ }
+ attrib_list = strippedAttribList.data();
+
+ if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+ int err = native_window_set_buffers_format(window, format);
+ if (err != 0) {
+ ALOGE("error setting native window pixel format: %s (%d)", strerror(-err), err);
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
+
+ android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace);
+ if (dataSpace != HAL_DATASPACE_UNKNOWN) {
+ err = native_window_set_buffers_data_space(window, dataSpace);
+ if (err != 0) {
+ ALOGE("error setting native window pixel dataSpace: %s (%d)", strerror(-err), err);
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
+ }
+ }
+
+ // the EGL spec requires that a new EGLSurface default to swap interval
+ // 1, so explicitly set that on the window here.
+ window->setSwapInterval(window, 1);
+
+ EGLSurface surface = createWindowSurfaceFunc(iDpy, config, window, attrib_list);
+ if (surface != EGL_NO_SURFACE) {
+ egl_surface_t* s = new egl_surface_t(dp.get(), config, window, surface,
+ getReportedColorSpace(colorSpace), cnx);
+ return s;
+ }
+
+ // EGLSurface creation failed
+ if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+ native_window_set_buffers_format(window, 0);
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ }
+ return EGL_NO_SURFACE;
+}
+
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay dpy, EGLConfig config,
+ NativeWindowType window,
+ const EGLint* attrib_list);
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC)(
+ EGLDisplay dpy, EGLConfig config, void* native_window, const EGLAttrib* attrib_list);
+
+EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window,
+ const EGLint* attrib_list) {
+ egl_connection_t* cnx = NULL;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ return eglCreateWindowSurfaceTmpl<
+ EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list,
+ cnx->egl.eglCreateWindowSurface);
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window,
+ const EGLAttrib* attrib_list) {
+ egl_connection_t* cnx = NULL;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglCreatePlatformWindowSurface) {
+ return eglCreateWindowSurfaceTmpl<EGLAttrib, PFNEGLCREATEPLATFORMWINDOWSURFACEPROC>(
+ dp, cnx, config, static_cast<ANativeWindow*>(native_window), attrib_list,
+ cnx->egl.eglCreatePlatformWindowSurface);
+ }
+ // driver doesn't support native function, return EGL_BAD_DISPLAY
+ ALOGE("Driver indicates EGL 1.5 support, but does not have "
+ "eglCreatePlatformWindowSurface");
+ return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
+ }
+
+ std::vector<EGLint> convertedAttribs;
+ convertAttribs(attrib_list, convertedAttribs);
+ if (cnx->egl.eglCreatePlatformWindowSurfaceEXT) {
+ return eglCreateWindowSurfaceTmpl<EGLint, PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(
+ dp, cnx, config, static_cast<ANativeWindow*>(native_window),
+ convertedAttribs.data(), cnx->egl.eglCreatePlatformWindowSurfaceEXT);
+ } else {
+ return eglCreateWindowSurfaceTmpl<
+ EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config,
+ static_cast<ANativeWindow*>(
+ native_window),
+ convertedAttribs.data(),
+ cnx->egl.eglCreateWindowSurface);
+ }
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePlatformPixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/,
+ void* /*native_pixmap*/,
+ const EGLAttrib* /*attrib_list*/) {
+ // Per EGL_KHR_platform_android:
+ // It is not valid to call eglCreatePlatformPixmapSurface with a <dpy> that
+ // belongs to the Android platform. Any such call fails and generates
+ // an EGL_BAD_PARAMETER error.
+
+ egl_connection_t* cnx = NULL;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/,
+ NativePixmapType /*pixmap*/, const EGLint* /*attrib_list*/) {
+ egl_connection_t* cnx = nullptr;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLSurface eglCreatePbufferSurfaceImpl( EGLDisplay dpy, EGLConfig config,
+ const EGLint *attrib_list)
+{
+ egl_connection_t* cnx = nullptr;
+ egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ EGLDisplay iDpy = dp->disp.dpy;
+ android_pixel_format format;
+ getNativePixelFormat(iDpy, cnx, config, &format);
+
+ // Select correct colorspace based on user's attribute list
+ EGLint colorSpace = EGL_UNKNOWN;
+ std::vector<EGLint> strippedAttribList;
+ if (!processAttributes(dp, nullptr, format, attrib_list, &colorSpace,
+ &strippedAttribList)) {
+ ALOGE("error invalid colorspace: %d", colorSpace);
+ return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ }
+ attrib_list = strippedAttribList.data();
+
+ EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
+ dp->disp.dpy, config, attrib_list);
+ if (surface != EGL_NO_SURFACE) {
+ egl_surface_t* s =
+ new egl_surface_t(dp.get(), config, nullptr, surface,
+ getReportedColorSpace(colorSpace), cnx);
+ return s;
+ }
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t * const s = get_surface(surface);
+ EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
+ if (result == EGL_TRUE) {
+ _s.terminate();
+ }
+ return result;
+}
+
+EGLBoolean eglQuerySurfaceImpl( EGLDisplay dpy, EGLSurface surface,
+ EGLint attribute, EGLint *value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const * const s = get_surface(surface);
+ if (s->getColorSpaceAttribute(attribute, value)) {
+ return EGL_TRUE;
+ } else if (s->getSmpte2086Attribute(attribute, value)) {
+ return EGL_TRUE;
+ } else if (s->getCta8613Attribute(attribute, value)) {
+ return EGL_TRUE;
+ }
+ return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value);
+}
+
+void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return;
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Contexts
+// ----------------------------------------------------------------------------
+
+EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config,
+ EGLContext share_list, const EGLint *attrib_list)
+{
+ egl_connection_t* cnx = nullptr;
+ const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (dp) {
+ if (share_list != EGL_NO_CONTEXT) {
+ if (!ContextRef(dp.get(), share_list).get()) {
+ return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
+ }
+ egl_context_t* const c = get_context(share_list);
+ share_list = c->context;
+ }
+ EGLContext context = cnx->egl.eglCreateContext(
+ dp->disp.dpy, config, share_list, attrib_list);
+ if (context != EGL_NO_CONTEXT) {
+ // figure out if it's a GLESv1 or GLESv2
+ int version = 0;
+ if (attrib_list) {
+ while (*attrib_list != EGL_NONE) {
+ GLint attr = *attrib_list++;
+ GLint value = *attrib_list++;
+ if (attr == EGL_CONTEXT_CLIENT_VERSION) {
+ if (value == 1) {
+ version = egl_connection_t::GLESv1_INDEX;
+ } else if (value == 2 || value == 3) {
+ version = egl_connection_t::GLESv2_INDEX;
+ }
+ }
+ };
+ }
+ egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
+ version);
+ return c;
+ }
+ }
+ return EGL_NO_CONTEXT;
+}
+
+EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp)
+ return EGL_FALSE;
+
+ ContextRef _c(dp.get(), ctx);
+ if (!_c.get())
+ return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+ egl_context_t * const c = get_context(ctx);
+ EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
+ if (result == EGL_TRUE) {
+ _c.terminate();
+ }
+ return result;
+}
+
+EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw,
+ EGLSurface read, EGLContext ctx)
+{
+ egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+
+ // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
+ // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
+ // a valid but uninitialized display.
+ if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
+ (draw != EGL_NO_SURFACE) ) {
+ if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+ }
+
+ // get a reference to the object passed in
+ ContextRef _c(dp.get(), ctx);
+ SurfaceRef _d(dp.get(), draw);
+ SurfaceRef _r(dp.get(), read);
+
+ // validate the context (if not EGL_NO_CONTEXT)
+ if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
+ // EGL_NO_CONTEXT is valid
+ return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+ }
+
+ // these are the underlying implementation's object
+ EGLContext impl_ctx = EGL_NO_CONTEXT;
+ EGLSurface impl_draw = EGL_NO_SURFACE;
+ EGLSurface impl_read = EGL_NO_SURFACE;
+
+ // these are our objects structs passed in
+ egl_context_t * c = nullptr;
+ egl_surface_t const * d = nullptr;
+ egl_surface_t const * r = nullptr;
+
+ // these are the current objects structs
+ egl_context_t * cur_c = get_context(getContext());
+
+ if (ctx != EGL_NO_CONTEXT) {
+ c = get_context(ctx);
+ impl_ctx = c->context;
+ } else {
+ // no context given, use the implementation of the current context
+ if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
+ // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
+ return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE);
+ }
+ if (cur_c == nullptr) {
+ // no current context
+ // not an error, there is just no current context.
+ return EGL_TRUE;
+ }
+ }
+
+ // retrieve the underlying implementation's draw EGLSurface
+ if (draw != EGL_NO_SURFACE) {
+ if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ d = get_surface(draw);
+ impl_draw = d->surface;
+ }
+
+ // retrieve the underlying implementation's read EGLSurface
+ if (read != EGL_NO_SURFACE) {
+ if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ r = get_surface(read);
+ impl_read = r->surface;
+ }
+
+
+ EGLBoolean result = dp->makeCurrent(c, cur_c,
+ draw, read, ctx,
+ impl_draw, impl_read, impl_ctx);
+
+ if (result == EGL_TRUE) {
+ if (c) {
+ setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
+ egl_tls_t::setContext(ctx);
+ _c.acquire();
+ _r.acquire();
+ _d.acquire();
+ } else {
+ setGLHooksThreadSpecific(&gHooksNoContext);
+ egl_tls_t::setContext(EGL_NO_CONTEXT);
+ }
+ } else {
+ // this will ALOGE the error
+ egl_connection_t* const cnx = &gEGLImpl;
+ result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE);
+ }
+ return result;
+}
+
+EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx,
+ EGLint attribute, EGLint *value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ ContextRef _c(dp.get(), ctx);
+ if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+ egl_context_t * const c = get_context(ctx);
+ return c->cnx->egl.eglQueryContext(
+ dp->disp.dpy, c->context, attribute, value);
+
+}
+
+EGLContext eglGetCurrentContextImpl(void)
+{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would correctly return EGL_NO_CONTEXT.
+ EGLContext ctx = getContext();
+ return ctx;
+}
+
+EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw)
+{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would correctly return EGL_NO_SURFACE.
+
+ EGLContext ctx = getContext();
+ if (ctx) {
+ egl_context_t const * const c = get_context(ctx);
+ if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
+ switch (readdraw) {
+ case EGL_READ: return c->read;
+ case EGL_DRAW: return c->draw;
+ default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ }
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLDisplay eglGetCurrentDisplayImpl(void)
+{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would correctly return EGL_NO_DISPLAY.
+
+ EGLContext ctx = getContext();
+ if (ctx) {
+ egl_context_t const * const c = get_context(ctx);
+ if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
+ return c->dpy;
+ }
+ return EGL_NO_DISPLAY;
+}
+
+EGLBoolean eglWaitGLImpl(void)
+{
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (!cnx->dso)
+ return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+ return cnx->egl.eglWaitGL();
+}
+
+EGLBoolean eglWaitNativeImpl(EGLint engine)
+{
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (!cnx->dso)
+ return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+ return cnx->egl.eglWaitNative(engine);
+}
+
+EGLint eglGetErrorImpl(void)
+{
+ EGLint err = EGL_SUCCESS;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso) {
+ err = cnx->egl.eglGetError();
+ }
+ if (err == EGL_SUCCESS) {
+ err = egl_tls_t::getError();
+ }
+ return err;
+}
+
+static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(
+ const char* procname) {
+ const egl_connection_t* cnx = &gEGLImpl;
+ void* proc = nullptr;
+
+ proc = dlsym(cnx->libEgl, procname);
+ if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+ proc = dlsym(cnx->libGles2, procname);
+ if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+ proc = dlsym(cnx->libGles1, procname);
+ if (proc) return (__eglMustCastToProperFunctionPointerType)proc;
+
+ return nullptr;
+}
+
+__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname)
+{
+ if (FILTER_EXTENSIONS(procname)) {
+ return nullptr;
+ }
+
+ __eglMustCastToProperFunctionPointerType addr;
+ addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap));
+ if (addr) return addr;
+
+ addr = findBuiltinWrapper(procname);
+ if (addr) return addr;
+
+ // this protects accesses to sGLExtentionMap and sGLExtentionSlot
+ pthread_mutex_lock(&sExtensionMapMutex);
+
+ /*
+ * Since eglGetProcAddress() is not associated to anything, it needs
+ * to return a function pointer that "works" regardless of what
+ * the current context is.
+ *
+ * For this reason, we return a "forwarder", a small stub that takes
+ * care of calling the function associated with the context
+ * currently bound.
+ *
+ * We first look for extensions we've already resolved, if we're seeing
+ * this extension for the first time, we go through all our
+ * implementations and call eglGetProcAddress() and record the
+ * result in the appropriate implementation hooks and return the
+ * address of the forwarder corresponding to that hook set.
+ *
+ */
+
+ const std::string name(procname);
+
+ auto& extentionMap = sGLExtentionMap;
+ auto pos = extentionMap.find(name);
+ addr = (pos != extentionMap.end()) ? pos->second : nullptr;
+ const int slot = sGLExtentionSlot;
+
+ ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
+ "no more slots for eglGetProcAddress(\"%s\")",
+ procname);
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ LayerLoader& layer_loader(LayerLoader::getInstance());
+
+ if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
+
+ if (cnx->dso && cnx->egl.eglGetProcAddress) {
+
+ // Extensions are independent of the bound context
+ addr = cnx->egl.eglGetProcAddress(procname);
+ if (addr) {
+
+ // purposefully track the bottom of the stack in extensionMap
+ extentionMap[name] = addr;
+
+ // Apply layers
+ addr = layer_loader.ApplyLayers(procname, addr);
+
+ // Track the top most entry point
+ cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+ addr = gExtensionForwarders[slot];
+ sGLExtentionSlot++;
+ }
+ }
+
+ } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) {
+
+ // We've seen this func before, but we tracked the bottom, so re-apply layers
+ // More layers might have been enabled
+ addr = layer_loader.ApplyLayers(procname, addr);
+
+ // Track the top most entry point
+ cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
+ cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr;
+ addr = gExtensionForwarders[slot];
+ }
+
+ pthread_mutex_unlock(&sExtensionMapMutex);
+ return addr;
+}
+
+class FrameCompletionThread {
+public:
+
+ static void queueSync(EGLSyncKHR sync) {
+ static FrameCompletionThread thread;
+
+ char name[64];
+
+ std::lock_guard<std::mutex> lock(thread.mMutex);
+ snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued);
+ ATRACE_NAME(name);
+
+ thread.mQueue.push_back(sync);
+ thread.mCondition.notify_one();
+ thread.mFramesQueued++;
+ ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size()));
+ }
+
+private:
+
+ FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {
+ std::thread thread(&FrameCompletionThread::loop, this);
+ thread.detach();
+ }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+ void loop() {
+ while (true) {
+ threadLoop();
+ }
+ }
+#pragma clang diagnostic pop
+
+ void threadLoop() {
+ EGLSyncKHR sync;
+ uint32_t frameNum;
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (mQueue.empty()) {
+ mCondition.wait(lock);
+ }
+ sync = mQueue[0];
+ frameNum = mFramesCompleted;
+ }
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ {
+ char name[64];
+ snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum);
+ ATRACE_NAME(name);
+
+ EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
+ if (result == EGL_FALSE) {
+ ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGE("FrameCompletion: timeout waiting for fence");
+ }
+ eglDestroySyncKHR(dpy, sync);
+ }
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mQueue.pop_front();
+ mFramesCompleted++;
+ ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size()));
+ }
+ }
+
+ uint32_t mFramesQueued;
+ uint32_t mFramesCompleted;
+ std::deque<EGLSyncKHR> mQueue;
+ std::condition_variable mCondition;
+ std::mutex mMutex;
+};
+
+EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw,
+ EGLint *rects, EGLint n_rects)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), draw);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t* const s = get_surface(draw);
+
+ if (CC_UNLIKELY(dp->traceGpuCompletion)) {
+ EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
+ if (sync != EGL_NO_SYNC_KHR) {
+ FrameCompletionThread::queueSync(sync);
+ }
+ }
+
+ if (CC_UNLIKELY(dp->finishOnSwap)) {
+ uint32_t pixel;
+ egl_context_t * const c = get_context( egl_tls_t::getContext() );
+ if (c) {
+ // glReadPixels() ensures that the frame is complete
+ s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
+ GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
+ }
+ }
+
+ if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+ if (!sendSurfaceMetadata(s)) {
+ native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL);
+ return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE);
+ }
+ }
+
+ if (n_rects == 0) {
+ return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+ }
+
+ std::vector<android_native_rect_t> androidRects((size_t)n_rects);
+ for (int r = 0; r < n_rects; ++r) {
+ int offset = r * 4;
+ int x = rects[offset];
+ int y = rects[offset + 1];
+ int width = rects[offset + 2];
+ int height = rects[offset + 3];
+ android_native_rect_t androidRect;
+ androidRect.left = x;
+ androidRect.top = y + height;
+ androidRect.right = x + width;
+ androidRect.bottom = y;
+ androidRects.push_back(androidRect);
+ }
+ if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+ native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(),
+ androidRects.size());
+ }
+
+ if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
+ return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
+ rects, n_rects);
+ } else {
+ return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+ }
+}
+
+EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface)
+{
+ return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0);
+}
+
+EGLBoolean eglCopyBuffersImpl( EGLDisplay dpy, EGLSurface surface,
+ NativePixmapType target)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const * const s = get_surface(surface);
+ return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
+}
+
+const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name)
+{
+ if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) {
+ // Return list of client extensions
+ return gClientExtensionString;
+ }
+
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return (const char *) nullptr;
+
+ switch (name) {
+ case EGL_VENDOR:
+ return dp->getVendorString();
+ case EGL_VERSION:
+ return dp->getVersionString();
+ case EGL_EXTENSIONS:
+ return dp->getExtensionString();
+ case EGL_CLIENT_APIS:
+ return dp->getClientApiString();
+ default:
+ break;
+ }
+ return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+}
+
+EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return (const char *) nullptr;
+
+ switch (name) {
+ case EGL_VENDOR:
+ return dp->disp.queryString.vendor;
+ case EGL_VERSION:
+ return dp->disp.queryString.version;
+ case EGL_EXTENSIONS:
+ return dp->disp.queryString.extensions;
+ case EGL_CLIENT_APIS:
+ return dp->disp.queryString.clientApi;
+ default:
+ break;
+ }
+ return setError(EGL_BAD_PARAMETER, (const char *)nullptr);
+}
+
+// ----------------------------------------------------------------------------
+// EGL 1.1
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglSurfaceAttribImpl(
+ EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t * const s = get_surface(surface);
+
+ if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
+ if (!s->getNativeWindow()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+ int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0);
+ return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ if (attribute == EGL_TIMESTAMPS_ANDROID) {
+ if (!s->getNativeWindow()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+ int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0);
+ return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ if (s->setSmpte2086Attribute(attribute, value)) {
+ return EGL_TRUE;
+ } else if (s->setCta8613Attribute(attribute, value)) {
+ return EGL_TRUE;
+ } else if (s->cnx->egl.eglSurfaceAttrib) {
+ return s->cnx->egl.eglSurfaceAttrib(
+ dp->disp.dpy, s->surface, attribute, value);
+ }
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglBindTexImageImpl(
+ EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const * const s = get_surface(surface);
+ if (s->cnx->egl.eglBindTexImage) {
+ return s->cnx->egl.eglBindTexImage(
+ dp->disp.dpy, s->surface, buffer);
+ }
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglReleaseTexImageImpl(
+ EGLDisplay dpy, EGLSurface surface, EGLint buffer)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const * const s = get_surface(surface);
+ if (s->cnx->egl.eglReleaseTexImage) {
+ return s->cnx->egl.eglReleaseTexImage(
+ dp->disp.dpy, s->surface, buffer);
+ }
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean res = EGL_TRUE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglSwapInterval) {
+ res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval);
+ }
+
+ return res;
+}
+
+
+// ----------------------------------------------------------------------------
+// EGL 1.2
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglWaitClientImpl(void)
+{
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (!cnx->dso)
+ return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE);
+
+ EGLBoolean res;
+ if (cnx->egl.eglWaitClient) {
+ res = cnx->egl.eglWaitClient();
+ } else {
+ res = cnx->egl.eglWaitGL();
+ }
+ return res;
+}
+
+EGLBoolean eglBindAPIImpl(EGLenum api)
+{
+ // bind this API on all EGLs
+ EGLBoolean res = EGL_TRUE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglBindAPI) {
+ res = cnx->egl.eglBindAPI(api);
+ }
+ return res;
+}
+
+EGLenum eglQueryAPIImpl(void)
+{
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglQueryAPI) {
+ return cnx->egl.eglQueryAPI();
+ }
+
+ // or, it can only be OpenGL ES
+ return EGL_OPENGL_ES_API;
+}
+
+EGLBoolean eglReleaseThreadImpl(void)
+{
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglReleaseThread) {
+ cnx->egl.eglReleaseThread();
+ }
+
+ // If there is context bound to the thread, release it
+ egl_display_t::loseCurrent(get_context(getContext()));
+
+ egl_tls_t::clearTLS();
+ return EGL_TRUE;
+}
+
+EGLSurface eglCreatePbufferFromClientBufferImpl(
+ EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
+ EGLConfig config, const EGLint *attrib_list)
+{
+ egl_connection_t* cnx = nullptr;
+ const egl_display_ptr dp = validate_display_connection(dpy, cnx);
+ if (!dp) return EGL_FALSE;
+ if (cnx->egl.eglCreatePbufferFromClientBuffer) {
+ return cnx->egl.eglCreatePbufferFromClientBuffer(
+ dp->disp.dpy, buftype, buffer, config, attrib_list);
+ }
+ return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 3
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface,
+ const EGLint *attrib_list)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const * const s = get_surface(surface);
+ if (s->cnx->egl.eglLockSurfaceKHR) {
+ return s->cnx->egl.eglLockSurfaceKHR(
+ dp->disp.dpy, s->surface, attrib_list);
+ }
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+}
+
+EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get())
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+
+ egl_surface_t const * const s = get_surface(surface);
+ if (s->cnx->egl.eglUnlockSurfaceKHR) {
+ return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
+ }
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+}
+
+// Note: EGLImageKHR and EGLImage are the same thing so no need
+// to templatize that.
+template <typename AttrType, typename FuncType>
+EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+ EGLClientBuffer buffer, const AttrType* attrib_list,
+ FuncType eglCreateImageFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_IMAGE_KHR;
+
+ ContextRef _c(dp.get(), ctx);
+ egl_context_t* const c = _c.get();
+
+ EGLImageKHR result = EGL_NO_IMAGE_KHR;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && eglCreateImageFunc) {
+ result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer,
+ attrib_list);
+ }
+ return result;
+}
+
+typedef EGLImage(EGLAPIENTRYP PFNEGLCREATEIMAGE)(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+ EGLClientBuffer buffer,
+ const EGLAttrib* attrib_list);
+
+EGLImageKHR eglCreateImageKHRImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+ EGLClientBuffer buffer, const EGLint* attrib_list) {
+ return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer,
+ attrib_list,
+ gEGLImpl.egl.eglCreateImageKHR);
+}
+
+EGLImage eglCreateImageImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer,
+ const EGLAttrib* attrib_list) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglCreateImage) {
+ return eglCreateImageTmpl<EGLAttrib, PFNEGLCREATEIMAGE>(dpy, ctx, target, buffer,
+ attrib_list,
+ cnx->egl.eglCreateImage);
+ }
+ // driver doesn't support native function, return EGL_BAD_DISPLAY
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateImage");
+ return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE);
+ }
+
+ std::vector<EGLint> convertedAttribs;
+ convertAttribs(attrib_list, convertedAttribs);
+ return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer,
+ convertedAttribs.data(),
+ gEGLImpl.egl.eglCreateImageKHR);
+}
+
+EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img,
+ PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && destroyImageFunc) {
+ result = destroyImageFunc(dp->disp.dpy, img);
+ }
+ return result;
+}
+
+EGLBoolean eglDestroyImageKHRImpl(EGLDisplay dpy, EGLImageKHR img) {
+ return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR);
+}
+
+EGLBoolean eglDestroyImageImpl(EGLDisplay dpy, EGLImageKHR img) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglDestroyImage) {
+ return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImage);
+ }
+ // driver doesn't support native function, return EGL_BAD_DISPLAY
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroyImage");
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ }
+
+ return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR);
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 5
+// ----------------------------------------------------------------------------
+
+// NOTE: EGLSyncKHR and EGLSync are identical, no need to templatize
+template <typename AttrType, typename FuncType>
+EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list,
+ FuncType eglCreateSyncFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_SYNC_KHR;
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ EGLSyncKHR result = EGL_NO_SYNC_KHR;
+ if (cnx->dso && eglCreateSyncFunc) {
+ result = eglCreateSyncFunc(dp->disp.dpy, type, attrib_list);
+ }
+ return result;
+}
+
+typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATESYNC)(EGLDisplay dpy, EGLenum type,
+ const EGLAttrib* attrib_list);
+
+EGLSyncKHR eglCreateSyncKHRImpl(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) {
+ return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, attrib_list,
+ gEGLImpl.egl.eglCreateSyncKHR);
+}
+
+EGLSync eglCreateSyncImpl(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglCreateSync) {
+ return eglCreateSyncTmpl<EGLAttrib, PFNEGLCREATESYNC>(dpy, type, attrib_list,
+ cnx->egl.eglCreateSync);
+ }
+ // driver doesn't support native function, return EGL_BAD_DISPLAY
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateSync");
+ return setError(EGL_BAD_DISPLAY, EGL_NO_SYNC);
+ }
+
+ std::vector<EGLint> convertedAttribs;
+ convertAttribs(attrib_list, convertedAttribs);
+ return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, convertedAttribs.data(),
+ cnx->egl.eglCreateSyncKHR);
+}
+
+EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync,
+ PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && eglDestroySyncFunc) {
+ result = eglDestroySyncFunc(dp->disp.dpy, sync);
+ }
+ return result;
+}
+
+EGLBoolean eglDestroySyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+ return eglDestroySyncTmpl(dpy, sync, gEGLImpl.egl.eglDestroySyncKHR);
+}
+
+EGLBoolean eglDestroySyncImpl(EGLDisplay dpy, EGLSyncKHR sync) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglDestroySync) {
+ return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySync);
+ }
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroySync");
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ }
+
+ return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySyncKHR);
+}
+
+EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && gEGLImpl.egl.eglSignalSyncKHR) {
+ result = gEGLImpl.egl.eglSignalSyncKHR(dp->disp.dpy, sync, mode);
+ }
+ return result;
+}
+
+EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout,
+ PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLint result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && eglClientWaitSyncFunc) {
+ result = eglClientWaitSyncFunc(dp->disp.dpy, sync, flags, timeout);
+ }
+ return result;
+}
+
+EGLint eglClientWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR);
+}
+
+EGLint eglClientWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglClientWaitSync) {
+ return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSync);
+ }
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglClientWaitSync");
+ return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+ }
+
+ return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR);
+}
+
+template <typename AttrType, typename FuncType>
+EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value,
+ FuncType eglGetSyncAttribFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && eglGetSyncAttribFunc) {
+ result = eglGetSyncAttribFunc(dp->disp.dpy, sync, attribute, value);
+ }
+ return result;
+}
+
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLGETSYNCATTRIB)(EGLDisplay dpy, EGLSync sync, EGLint attribute,
+ EGLAttrib* value);
+
+EGLBoolean eglGetSyncAttribImpl(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglGetSyncAttrib) {
+ return eglGetSyncAttribTmpl<EGLAttrib, PFNEGLGETSYNCATTRIB>(dpy, sync, attribute, value,
+ cnx->egl.eglGetSyncAttrib);
+ }
+ ALOGE("Driver indicates EGL 1.5 support, but does not have eglGetSyncAttrib");
+ return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+ }
+
+ // Fallback to KHR, ask for EGLint attribute and cast back to EGLAttrib
+ EGLint attribValue;
+ EGLBoolean ret =
+ eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute,
+ &attribValue,
+ gEGLImpl.egl
+ .eglGetSyncAttribKHR);
+ if (ret) {
+ *value = static_cast<EGLAttrib>(attribValue);
+ }
+ return ret;
+}
+
+EGLBoolean eglGetSyncAttribKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute,
+ EGLint* value) {
+ return eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute, value,
+ gEGLImpl.egl
+ .eglGetSyncAttribKHR);
+}
+
+EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_STREAM_KHR;
+
+ EGLStreamKHR result = EGL_NO_STREAM_KHR;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglCreateStreamKHR) {
+ result = cnx->egl.eglCreateStreamKHR(
+ dp->disp.dpy, attrib_list);
+ }
+ return result;
+}
+
+EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglDestroyStreamKHR) {
+ result = cnx->egl.eglDestroyStreamKHR(
+ dp->disp.dpy, stream);
+ }
+ return result;
+}
+
+EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+ EGLenum attribute, EGLint value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglStreamAttribKHR) {
+ result = cnx->egl.eglStreamAttribKHR(
+ dp->disp.dpy, stream, attribute, value);
+ }
+ return result;
+}
+
+EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+ EGLenum attribute, EGLint *value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglQueryStreamKHR) {
+ result = cnx->egl.eglQueryStreamKHR(
+ dp->disp.dpy, stream, attribute, value);
+ }
+ return result;
+}
+
+EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+ EGLenum attribute, EGLuint64KHR *value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) {
+ result = cnx->egl.eglQueryStreamu64KHR(
+ dp->disp.dpy, stream, attribute, value);
+ }
+ return result;
+}
+
+EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream,
+ EGLenum attribute, EGLTimeKHR *value)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) {
+ result = cnx->egl.eglQueryStreamTimeKHR(
+ dp->disp.dpy, stream, attribute, value);
+ }
+ return result;
+}
+
+EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config,
+ EGLStreamKHR stream, const EGLint *attrib_list)
+{
+ egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_SURFACE;
+
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) {
+ EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(
+ dp->disp.dpy, config, stream, attrib_list);
+ if (surface != EGL_NO_SURFACE) {
+ egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface,
+ EGL_GL_COLORSPACE_LINEAR_KHR, cnx);
+ return s;
+ }
+ }
+ return EGL_NO_SURFACE;
+}
+
+EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy,
+ EGLStreamKHR stream)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) {
+ result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(
+ dp->disp.dpy, stream);
+ }
+ return result;
+}
+
+EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy,
+ EGLStreamKHR stream)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) {
+ result = cnx->egl.eglStreamConsumerAcquireKHR(
+ dp->disp.dpy, stream);
+ }
+ return result;
+}
+
+EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy,
+ EGLStreamKHR stream)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+
+ EGLBoolean result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) {
+ result = cnx->egl.eglStreamConsumerReleaseKHR(
+ dp->disp.dpy, stream);
+ }
+ return result;
+}
+
+EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(
+ EGLDisplay dpy, EGLStreamKHR stream)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR;
+
+ EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) {
+ result = cnx->egl.eglGetStreamFileDescriptorKHR(
+ dp->disp.dpy, stream);
+ }
+ return result;
+}
+
+EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(
+ EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_STREAM_KHR;
+
+ EGLStreamKHR result = EGL_NO_STREAM_KHR;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) {
+ result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(
+ dp->disp.dpy, file_descriptor);
+ }
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 15
+// ----------------------------------------------------------------------------
+
+// Need to template function type because return type is different
+template <typename ReturnType, typename FuncType>
+ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags,
+ FuncType eglWaitSyncFunc) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_FALSE;
+ ReturnType result = EGL_FALSE;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && eglWaitSyncFunc) {
+ result = eglWaitSyncFunc(dp->disp.dpy, sync, flags);
+ }
+ return result;
+}
+
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLWAITSYNC)(EGLDisplay dpy, EGLSync sync, EGLint flags);
+
+EGLint eglWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ return eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags,
+ cnx->egl.eglWaitSyncKHR);
+}
+
+EGLBoolean eglWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags) {
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) {
+ if (cnx->egl.eglWaitSync) {
+ return eglWaitSyncTmpl<EGLBoolean, PFNEGLWAITSYNC>(dpy, sync, flags,
+ cnx->egl.eglWaitSync);
+ }
+ return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE);
+ }
+
+ return static_cast<EGLBoolean>(
+ eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags,
+ cnx->egl.eglWaitSyncKHR));
+}
+
+// ----------------------------------------------------------------------------
+// ANDROID extensions
+// ----------------------------------------------------------------------------
+
+EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
+
+ EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
+ egl_connection_t* const cnx = &gEGLImpl;
+ if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) {
+ result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync);
+ }
+ return result;
+}
+
+EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLnsecsANDROID time)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return EGL_FALSE;
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+ native_window_set_buffers_timestamp(s->getNativeWindow(), time);
+
+ return EGL_TRUE;
+}
+
+EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) {
+ // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus
+ // this function cannot be implemented when this libEGL is built for
+ // vendors.
+#ifndef __ANDROID_VNDK__
+ if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
+ return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer));
+#else
+ return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr);
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// NVIDIA extensions
+// ----------------------------------------------------------------------------
+EGLuint64NV eglGetSystemTimeFrequencyNVImpl()
+{
+ EGLuint64NV ret = 0;
+ egl_connection_t* const cnx = &gEGLImpl;
+
+ if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) {
+ return cnx->egl.eglGetSystemTimeFrequencyNV();
+ }
+
+ return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+}
+
+EGLuint64NV eglGetSystemTimeNVImpl()
+{
+ EGLuint64NV ret = 0;
+ egl_connection_t* const cnx = &gEGLImpl;
+
+ if (cnx->dso && cnx->egl.eglGetSystemTimeNV) {
+ return cnx->egl.eglGetSystemTimeNV();
+ }
+
+ return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0);
+}
+
+// ----------------------------------------------------------------------------
+// Partial update extension
+// ----------------------------------------------------------------------------
+EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLint *rects, EGLint n_rects)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+ if (s->cnx->egl.eglSetDamageRegionKHR) {
+ return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
+ rects, n_rects);
+ }
+
+ return EGL_FALSE;
+}
+
+EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLuint64KHR *frameId) {
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ if (!s->getNativeWindow()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ uint64_t nextFrameId = 0;
+ int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId);
+
+ if (ret != 0) {
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ ALOGE("eglGetNextFrameId: Unexpected error.");
+ return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+ }
+
+ *frameId = nextFrameId;
+ return EGL_TRUE;
+}
+
+EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ if (!s->getNativeWindow()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ nsecs_t* compositeDeadline = nullptr;
+ nsecs_t* compositeInterval = nullptr;
+ nsecs_t* compositeToPresentLatency = nullptr;
+
+ for (int i = 0; i < numTimestamps; i++) {
+ switch (names[i]) {
+ case EGL_COMPOSITE_DEADLINE_ANDROID:
+ compositeDeadline = &values[i];
+ break;
+ case EGL_COMPOSITE_INTERVAL_ANDROID:
+ compositeInterval = &values[i];
+ break;
+ case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+ compositeToPresentLatency = &values[i];
+ break;
+ default:
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ }
+ }
+
+ int ret = native_window_get_compositor_timing(s->getNativeWindow(),
+ compositeDeadline, compositeInterval, compositeToPresentLatency);
+
+ switch (ret) {
+ case 0:
+ return EGL_TRUE;
+ case -ENOSYS:
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ default:
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ ALOGE("eglGetCompositorTiming: Unexpected error.");
+ return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+ }
+}
+
+EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(
+ EGLDisplay dpy, EGLSurface surface, EGLint name)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ ANativeWindow* window = s->getNativeWindow();
+ if (!window) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ switch (name) {
+ case EGL_COMPOSITE_DEADLINE_ANDROID:
+ case EGL_COMPOSITE_INTERVAL_ANDROID:
+ case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+ return EGL_TRUE;
+ default:
+ return EGL_FALSE;
+ }
+}
+
+EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface,
+ EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
+ EGLnsecsANDROID *values)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ if (!s->getNativeWindow()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ nsecs_t* requestedPresentTime = nullptr;
+ nsecs_t* acquireTime = nullptr;
+ nsecs_t* latchTime = nullptr;
+ nsecs_t* firstRefreshStartTime = nullptr;
+ nsecs_t* gpuCompositionDoneTime = nullptr;
+ nsecs_t* lastRefreshStartTime = nullptr;
+ nsecs_t* displayPresentTime = nullptr;
+ nsecs_t* dequeueReadyTime = nullptr;
+ nsecs_t* releaseTime = nullptr;
+
+ for (int i = 0; i < numTimestamps; i++) {
+ switch (timestamps[i]) {
+ case EGL_REQUESTED_PRESENT_TIME_ANDROID:
+ requestedPresentTime = &values[i];
+ break;
+ case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+ acquireTime = &values[i];
+ break;
+ case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+ latchTime = &values[i];
+ break;
+ case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+ firstRefreshStartTime = &values[i];
+ break;
+ case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+ lastRefreshStartTime = &values[i];
+ break;
+ case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
+ gpuCompositionDoneTime = &values[i];
+ break;
+ case EGL_DISPLAY_PRESENT_TIME_ANDROID:
+ displayPresentTime = &values[i];
+ break;
+ case EGL_DEQUEUE_READY_TIME_ANDROID:
+ dequeueReadyTime = &values[i];
+ break;
+ case EGL_READS_DONE_TIME_ANDROID:
+ releaseTime = &values[i];
+ break;
+ default:
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ }
+ }
+
+ int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId,
+ requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
+ lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime,
+ dequeueReadyTime, releaseTime);
+
+ switch (ret) {
+ case 0:
+ return EGL_TRUE;
+ case -ENOENT:
+ return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
+ case -ENOSYS:
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ case -EINVAL:
+ return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+ default:
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ ALOGE("eglGetFrameTimestamps: Unexpected error.");
+ return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE);
+ }
+}
+
+EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(
+ EGLDisplay dpy, EGLSurface surface, EGLint timestamp)
+{
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE);
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ ANativeWindow* window = s->getNativeWindow();
+ if (!window) {
+ return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
+ }
+
+ switch (timestamp) {
+ case EGL_COMPOSITE_DEADLINE_ANDROID:
+ case EGL_COMPOSITE_INTERVAL_ANDROID:
+ case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID:
+ case EGL_REQUESTED_PRESENT_TIME_ANDROID:
+ case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+ case EGL_COMPOSITION_LATCH_TIME_ANDROID:
+ case EGL_FIRST_COMPOSITION_START_TIME_ANDROID:
+ case EGL_LAST_COMPOSITION_START_TIME_ANDROID:
+ case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID:
+ case EGL_DEQUEUE_READY_TIME_ANDROID:
+ case EGL_READS_DONE_TIME_ANDROID:
+ return EGL_TRUE;
+ case EGL_DISPLAY_PRESENT_TIME_ANDROID: {
+ int value = 0;
+ window->query(window,
+ NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
+ return value == 0 ? EGL_FALSE : EGL_TRUE;
+ }
+ default:
+ return EGL_FALSE;
+ }
+}
+
+const GLubyte * glGetStringImpl(GLenum name) {
+ const GLubyte * ret = egl_get_string_for_current_context(name);
+ if (ret == NULL) {
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if(_c) ret = _c->glGetString(name);
+ }
+ return ret;
+}
+
+const GLubyte * glGetStringiImpl(GLenum name, GLuint index) {
+ const GLubyte * ret = egl_get_string_for_current_context(name, index);
+ if (ret == NULL) {
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if(_c) ret = _c->glGetStringi(name, index);
+ }
+ return ret;
+}
+
+void glGetBooleanvImpl(GLenum pname, GLboolean * data) {
+ if (pname == GL_NUM_EXTENSIONS) {
+ int num_exts = egl_get_num_extensions_for_current_context();
+ if (num_exts >= 0) {
+ *data = num_exts > 0 ? GL_TRUE : GL_FALSE;
+ return;
+ }
+ }
+
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if (_c) _c->glGetBooleanv(pname, data);
+}
+
+void glGetFloatvImpl(GLenum pname, GLfloat * data) {
+ if (pname == GL_NUM_EXTENSIONS) {
+ int num_exts = egl_get_num_extensions_for_current_context();
+ if (num_exts >= 0) {
+ *data = (GLfloat)num_exts;
+ return;
+ }
+ }
+
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if (_c) _c->glGetFloatv(pname, data);
+}
+
+void glGetIntegervImpl(GLenum pname, GLint * data) {
+ if (pname == GL_NUM_EXTENSIONS) {
+ int num_exts = egl_get_num_extensions_for_current_context();
+ if (num_exts >= 0) {
+ *data = (GLint)num_exts;
+ return;
+ }
+ }
+
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if (_c) _c->glGetIntegerv(pname, data);
+}
+
+void glGetInteger64vImpl(GLenum pname, GLint64 * data) {
+ if (pname == GL_NUM_EXTENSIONS) {
+ int num_exts = egl_get_num_extensions_for_current_context();
+ if (num_exts >= 0) {
+ *data = (GLint64)num_exts;
+ return;
+ }
+ }
+
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
+ if (_c) _c->glGetInteger64v(pname, data);
+}
+
+struct implementation_map_t {
+ const char* name;
+ EGLFuncPointer address;
+};
+
+static const implementation_map_t sPlatformImplMap[] = {
+ // clang-format off
+ { "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl },
+ { "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl },
+ { "eglInitialize", (EGLFuncPointer)&eglInitializeImpl },
+ { "eglTerminate", (EGLFuncPointer)&eglTerminateImpl },
+ { "eglGetConfigs", (EGLFuncPointer)&eglGetConfigsImpl },
+ { "eglChooseConfig", (EGLFuncPointer)&eglChooseConfigImpl },
+ { "eglGetConfigAttrib", (EGLFuncPointer)&eglGetConfigAttribImpl },
+ { "eglCreateWindowSurface", (EGLFuncPointer)&eglCreateWindowSurfaceImpl },
+ { "eglCreatePixmapSurface", (EGLFuncPointer)&eglCreatePixmapSurfaceImpl },
+ { "eglCreatePlatformWindowSurface", (EGLFuncPointer)&eglCreatePlatformWindowSurfaceImpl },
+ { "eglCreatePlatformPixmapSurface", (EGLFuncPointer)&eglCreatePlatformPixmapSurfaceImpl },
+ { "eglCreatePbufferSurface", (EGLFuncPointer)&eglCreatePbufferSurfaceImpl },
+ { "eglDestroySurface", (EGLFuncPointer)&eglDestroySurfaceImpl },
+ { "eglQuerySurface", (EGLFuncPointer)&eglQuerySurfaceImpl },
+ { "eglBeginFrame", (EGLFuncPointer)&eglBeginFrameImpl },
+ { "eglCreateContext", (EGLFuncPointer)&eglCreateContextImpl },
+ { "eglDestroyContext", (EGLFuncPointer)&eglDestroyContextImpl },
+ { "eglMakeCurrent", (EGLFuncPointer)&eglMakeCurrentImpl },
+ { "eglQueryContext", (EGLFuncPointer)&eglQueryContextImpl },
+ { "eglGetCurrentContext", (EGLFuncPointer)&eglGetCurrentContextImpl },
+ { "eglGetCurrentSurface", (EGLFuncPointer)&eglGetCurrentSurfaceImpl },
+ { "eglGetCurrentDisplay", (EGLFuncPointer)&eglGetCurrentDisplayImpl },
+ { "eglWaitGL", (EGLFuncPointer)&eglWaitGLImpl },
+ { "eglWaitNative", (EGLFuncPointer)&eglWaitNativeImpl },
+ { "eglGetError", (EGLFuncPointer)&eglGetErrorImpl },
+ { "eglSwapBuffersWithDamageKHR", (EGLFuncPointer)&eglSwapBuffersWithDamageKHRImpl },
+ { "eglGetProcAddress", (EGLFuncPointer)&eglGetProcAddressImpl },
+ { "eglSwapBuffers", (EGLFuncPointer)&eglSwapBuffersImpl },
+ { "eglCopyBuffers", (EGLFuncPointer)&eglCopyBuffersImpl },
+ { "eglQueryString", (EGLFuncPointer)&eglQueryStringImpl },
+ { "eglQueryStringImplementationANDROID", (EGLFuncPointer)&eglQueryStringImplementationANDROIDImpl },
+ { "eglSurfaceAttrib", (EGLFuncPointer)&eglSurfaceAttribImpl },
+ { "eglBindTexImage", (EGLFuncPointer)&eglBindTexImageImpl },
+ { "eglReleaseTexImage", (EGLFuncPointer)&eglReleaseTexImageImpl },
+ { "eglSwapInterval", (EGLFuncPointer)&eglSwapIntervalImpl },
+ { "eglWaitClient", (EGLFuncPointer)&eglWaitClientImpl },
+ { "eglBindAPI", (EGLFuncPointer)&eglBindAPIImpl },
+ { "eglQueryAPI", (EGLFuncPointer)&eglQueryAPIImpl },
+ { "eglReleaseThread", (EGLFuncPointer)&eglReleaseThreadImpl },
+ { "eglCreatePbufferFromClientBuffer", (EGLFuncPointer)&eglCreatePbufferFromClientBufferImpl },
+ { "eglLockSurfaceKHR", (EGLFuncPointer)&eglLockSurfaceKHRImpl },
+ { "eglUnlockSurfaceKHR", (EGLFuncPointer)&eglUnlockSurfaceKHRImpl },
+ { "eglCreateImageKHR", (EGLFuncPointer)&eglCreateImageKHRImpl },
+ { "eglDestroyImageKHR", (EGLFuncPointer)&eglDestroyImageKHRImpl },
+ { "eglCreateImage", (EGLFuncPointer)&eglCreateImageImpl },
+ { "eglDestroyImage", (EGLFuncPointer)&eglDestroyImageImpl },
+ { "eglCreateSync", (EGLFuncPointer)&eglCreateSyncImpl },
+ { "eglDestroySync", (EGLFuncPointer)&eglDestroySyncImpl },
+ { "eglClientWaitSync", (EGLFuncPointer)&eglClientWaitSyncImpl },
+ { "eglGetSyncAttrib", (EGLFuncPointer)&eglGetSyncAttribImpl },
+ { "eglCreateSyncKHR", (EGLFuncPointer)&eglCreateSyncKHRImpl },
+ { "eglDestroySyncKHR", (EGLFuncPointer)&eglDestroySyncKHRImpl },
+ { "eglSignalSyncKHR", (EGLFuncPointer)&eglSignalSyncKHRImpl },
+ { "eglClientWaitSyncKHR", (EGLFuncPointer)&eglClientWaitSyncKHRImpl },
+ { "eglGetSyncAttribKHR", (EGLFuncPointer)&eglGetSyncAttribKHRImpl },
+ { "eglCreateStreamKHR", (EGLFuncPointer)&eglCreateStreamKHRImpl },
+ { "eglDestroyStreamKHR", (EGLFuncPointer)&eglDestroyStreamKHRImpl },
+ { "eglStreamAttribKHR", (EGLFuncPointer)&eglStreamAttribKHRImpl },
+ { "eglQueryStreamKHR", (EGLFuncPointer)&eglQueryStreamKHRImpl },
+ { "eglQueryStreamu64KHR", (EGLFuncPointer)&eglQueryStreamu64KHRImpl },
+ { "eglQueryStreamTimeKHR", (EGLFuncPointer)&eglQueryStreamTimeKHRImpl },
+ { "eglCreateStreamProducerSurfaceKHR", (EGLFuncPointer)&eglCreateStreamProducerSurfaceKHRImpl },
+ { "eglStreamConsumerGLTextureExternalKHR", (EGLFuncPointer)&eglStreamConsumerGLTextureExternalKHRImpl },
+ { "eglStreamConsumerAcquireKHR", (EGLFuncPointer)&eglStreamConsumerAcquireKHRImpl },
+ { "eglStreamConsumerReleaseKHR", (EGLFuncPointer)&eglStreamConsumerReleaseKHRImpl },
+ { "eglGetStreamFileDescriptorKHR", (EGLFuncPointer)&eglGetStreamFileDescriptorKHRImpl },
+ { "eglCreateStreamFromFileDescriptorKHR", (EGLFuncPointer)&eglCreateStreamFromFileDescriptorKHRImpl },
+ { "eglWaitSync", (EGLFuncPointer)&eglWaitSyncImpl },
+ { "eglWaitSyncKHR", (EGLFuncPointer)&eglWaitSyncKHRImpl },
+ { "eglDupNativeFenceFDANDROID", (EGLFuncPointer)&eglDupNativeFenceFDANDROIDImpl },
+ { "eglPresentationTimeANDROID", (EGLFuncPointer)&eglPresentationTimeANDROIDImpl },
+ { "eglGetNativeClientBufferANDROID", (EGLFuncPointer)&eglGetNativeClientBufferANDROIDImpl },
+ { "eglGetSystemTimeFrequencyNV", (EGLFuncPointer)&eglGetSystemTimeFrequencyNVImpl },
+ { "eglGetSystemTimeNV", (EGLFuncPointer)&eglGetSystemTimeNVImpl },
+ { "eglSetDamageRegionKHR", (EGLFuncPointer)&eglSetDamageRegionKHRImpl },
+ { "eglGetNextFrameIdANDROID", (EGLFuncPointer)&eglGetNextFrameIdANDROIDImpl },
+ { "eglGetCompositorTimingANDROID", (EGLFuncPointer)&eglGetCompositorTimingANDROIDImpl },
+ { "eglGetCompositorTimingSupportedANDROID", (EGLFuncPointer)&eglGetCompositorTimingSupportedANDROIDImpl },
+ { "eglGetFrameTimestampsANDROID", (EGLFuncPointer)&eglGetFrameTimestampsANDROIDImpl },
+ { "eglGetFrameTimestampSupportedANDROID", (EGLFuncPointer)&eglGetFrameTimestampSupportedANDROIDImpl },
+ { "glGetString", (EGLFuncPointer)&glGetStringImpl },
+ { "glGetStringi", (EGLFuncPointer)&glGetStringiImpl },
+ { "glGetBooleanv", (EGLFuncPointer)&glGetBooleanvImpl },
+ { "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl },
+ { "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl },
+ { "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl },
+ // clang-format on
+};
+
+EGLFuncPointer FindPlatformImplAddr(const char* name)
+{
+ static const bool DEBUG = false;
+
+ if (name == nullptr) {
+ ALOGV("FindPlatformImplAddr called with null name");
+ return nullptr;
+ }
+
+ for (int i = 0; i < NELEM(sPlatformImplMap); i++) {
+ if (sPlatformImplMap[i].name == nullptr) {
+ ALOGV("FindPlatformImplAddr found nullptr for sPlatformImplMap[%i].name (%s)", i, name);
+ return nullptr;
+ }
+ if (!strcmp(name, sPlatformImplMap[i].name)) {
+ ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name);
+ return sPlatformImplMap[i].address;
+ }
+ }
+
+ ALOGV("FindPlatformImplAddr did not find an entry for %s", name);
+ return nullptr;
+}
+} // namespace android
diff --git a/opengl/libs/EGL/egl_platform_entries.h b/opengl/libs/EGL/egl_platform_entries.h
new file mode 100644
index 0000000..85b1db3
--- /dev/null
+++ b/opengl/libs/EGL/egl_platform_entries.h
@@ -0,0 +1,32 @@
+/*
+ * 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_EGLAPI_H
+#define ANDROID_EGLAPI_H
+
+#include <EGL/egl.h>
+
+typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
+
+namespace android {
+
+EGLint eglGetErrorImpl();
+EGLFuncPointer FindPlatformImplAddr(const char* name);
+
+}; // namespace android
+
+#endif // ANDROID_EGLAPI_H
+
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index b57c357..aaecb62 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -21,6 +21,7 @@
#include <cutils/properties.h>
#include <log/log.h>
#include "CallStack.h"
+#include "egl_platform_entries.h"
namespace android {
@@ -137,7 +138,7 @@
void egl_tls_t::clearError() {
// This must clear the error from all the underlying EGL implementations as
// well as the EGL wrapper layer.
- eglGetError();
+ android::eglGetErrorImpl();
}
EGLint egl_tls_t::getError() {
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 449ffc7..7c710d5 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -18,9 +18,13 @@
#define ANDROID_EGLDEFS_H
#include "../hooks.h"
+#include "egl_platform_entries.h"
+
+#include <log/log.h>
#define VERSION_MAJOR 1
#define VERSION_MINOR 4
+#define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
// ----------------------------------------------------------------------------
namespace android {
@@ -31,19 +35,43 @@
// ----------------------------------------------------------------------------
+extern char const * const platform_names[];
+
+// clang-format off
struct egl_connection_t {
enum {
GLESv1_INDEX = 0,
GLESv2_INDEX = 1
};
- inline egl_connection_t() : dso(nullptr) { }
+ inline egl_connection_t() : dso(nullptr) {
+
+ char const* const* entries = platform_names;
+ EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform);
+ while (*entries) {
+ const char* name = *entries;
+ EGLFuncPointer f = FindPlatformImplAddr(name);
+
+ if (f == nullptr) {
+ // If no entry found, update the lookup table: sPlatformImplMap
+ ALOGE("No entry found in platform lookup table for %s", name);
+ }
+
+ *curr++ = f;
+ entries++;
+ }
+ }
+
void * dso;
gl_hooks_t * hooks[2];
EGLint major;
EGLint minor;
+ EGLint driverVersion;
egl_t egl;
+ // Functions implemented or redirected by platform libraries
+ platform_impl_t platform;
+
void* libEgl;
void* libGles1;
void* libGles2;
@@ -52,6 +80,7 @@
EGLint angleBackend;
void* vendorEGL;
};
+// clang-format on
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index f7fde96..65f50f5 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -301,71 +301,31 @@
}
const GLubyte * glGetString(GLenum name) {
- const GLubyte * ret = egl_get_string_for_current_context(name);
- if (ret == NULL) {
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if(_c) ret = _c->glGetString(name);
- }
- return ret;
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetString(name);
}
const GLubyte * glGetStringi(GLenum name, GLuint index) {
- const GLubyte * ret = egl_get_string_for_current_context(name, index);
- if (ret == NULL) {
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if(_c) ret = _c->glGetStringi(name, index);
- }
- return ret;
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetStringi(name, index);
}
void glGetBooleanv(GLenum pname, GLboolean * data) {
- if (pname == GL_NUM_EXTENSIONS) {
- int num_exts = egl_get_num_extensions_for_current_context();
- if (num_exts >= 0) {
- *data = num_exts > 0 ? GL_TRUE : GL_FALSE;
- return;
- }
- }
-
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if (_c) _c->glGetBooleanv(pname, data);
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetBooleanv(pname, data);
}
void glGetFloatv(GLenum pname, GLfloat * data) {
- if (pname == GL_NUM_EXTENSIONS) {
- int num_exts = egl_get_num_extensions_for_current_context();
- if (num_exts >= 0) {
- *data = (GLfloat)num_exts;
- return;
- }
- }
-
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if (_c) _c->glGetFloatv(pname, data);
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetFloatv(pname, data);
}
void glGetIntegerv(GLenum pname, GLint * data) {
- if (pname == GL_NUM_EXTENSIONS) {
- int num_exts = egl_get_num_extensions_for_current_context();
- if (num_exts >= 0) {
- *data = (GLint)num_exts;
- return;
- }
- }
-
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if (_c) _c->glGetIntegerv(pname, data);
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetIntegerv(pname, data);
}
void glGetInteger64v(GLenum pname, GLint64 * data) {
- if (pname == GL_NUM_EXTENSIONS) {
- int num_exts = egl_get_num_extensions_for_current_context();
- if (num_exts >= 0) {
- *data = (GLint64)num_exts;
- return;
- }
- }
-
- gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
- if (_c) _c->glGetInteger64v(pname, data);
+ egl_connection_t* const cnx = egl_get_connection();
+ return cnx->platform.glGetInteger64v(pname, data);
}
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index a8855ef..0af0501 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -21,15 +21,18 @@
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>
+#include "EGL/egldefs.h"
#include "hooks.h"
// ----------------------------------------------------------------------------
namespace android {
// ----------------------------------------------------------------------------
+
EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name);
EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index);
EGLAPI GLint egl_get_num_extensions_for_current_context();
+EGLAPI egl_connection_t* egl_get_connection();
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index 81dbe0e..63a0e14 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -59,6 +59,10 @@
#define GL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__);
#define EGL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__);
+struct platform_impl_t {
+ #include "platform_entries.in"
+};
+
struct egl_t {
#include "EGL/egl_entries.in"
};
diff --git a/opengl/libs/platform_entries.in b/opengl/libs/platform_entries.in
new file mode 100644
index 0000000..4673411
--- /dev/null
+++ b/opengl/libs/platform_entries.in
@@ -0,0 +1,86 @@
+EGL_ENTRY(EGLDisplay, eglGetDisplay, EGLNativeDisplayType)
+EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, EGLNativeDisplayType, const EGLAttrib*)
+EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*)
+EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay)
+EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*)
+EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*)
+EGL_ENTRY(EGLBoolean, eglGetConfigAttrib, EGLDisplay, EGLConfig, EGLint, EGLint*)
+EGL_ENTRY(EGLSurface, eglCreateWindowSurface, EGLDisplay, EGLConfig, NativeWindowType, const EGLint*)
+EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*)
+EGL_ENTRY(EGLSurface, eglCreatePixmapSurface, EGLDisplay, EGLConfig, NativePixmapType, const EGLint*)
+EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*)
+EGL_ENTRY(EGLSurface, eglCreatePbufferSurface, EGLDisplay, EGLConfig, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroySurface, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglQuerySurface, EGLDisplay, EGLSurface, EGLint, EGLint*)
+EGL_ENTRY(void, eglBeginFrame, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLContext, eglCreateContext, EGLDisplay, EGLConfig, EGLContext, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyContext, EGLDisplay, EGLContext)
+EGL_ENTRY(EGLBoolean, eglMakeCurrent, EGLDisplay, EGLSurface, EGLSurface, EGLContext)
+EGL_ENTRY(EGLBoolean, eglQueryContext, EGLDisplay, EGLContext, EGLint, EGLint*)
+EGL_ENTRY(EGLContext, eglGetCurrentContext, void)
+EGL_ENTRY(EGLSurface, eglGetCurrentSurface, EGLint)
+EGL_ENTRY(EGLDisplay, eglGetCurrentDisplay, void)
+EGL_ENTRY(EGLBoolean, eglWaitGL, void)
+EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint)
+EGL_ENTRY(EGLint, eglGetError, void)
+EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char*)
+EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint*, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType)
+EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint)
+EGL_ENTRY(const char*, eglQueryStringImplementationANDROID, EGLDisplay, EGLint)
+EGL_ENTRY(EGLBoolean, eglSurfaceAttrib, EGLDisplay, EGLSurface, EGLint, EGLint)
+EGL_ENTRY(EGLBoolean, eglBindTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglReleaseTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapInterval, EGLDisplay, EGLint)
+EGL_ENTRY(EGLBoolean, eglWaitClient, void)
+EGL_ENTRY(EGLBoolean, eglBindAPI, EGLenum)
+EGL_ENTRY(EGLenum, eglQueryAPI, void)
+EGL_ENTRY(EGLBoolean, eglReleaseThread, void)
+EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglUnlockSurfaceKHR, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib*)
+EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage)
+EGL_ENTRY(EGLImageKHR, eglCreateImageKHR, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyImageKHR, EGLDisplay, EGLImageKHR)
+EGL_ENTRY(EGLSync, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib*)
+EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync)
+EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSyncKHR, EGLint, EGLAttrib*)
+EGL_ENTRY(EGLSyncKHR, eglCreateSyncKHR, EGLDisplay, EGLenum, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroySyncKHR, EGLDisplay, EGLSyncKHR)
+EGL_ENTRY(EGLBoolean, eglSignalSyncKHR, EGLDisplay, EGLSyncKHR, EGLenum)
+EGL_ENTRY(EGLint, eglClientWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean, eglGetSyncAttribKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLint*)
+EGL_ENTRY(EGLStreamKHR, eglCreateStreamKHR, EGLDisplay, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglDestroyStreamKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamAttribKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint)
+EGL_ENTRY(EGLBoolean, eglQueryStreamKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint*)
+EGL_ENTRY(EGLBoolean, eglQueryStreamu64KHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLuint64KHR*)
+EGL_ENTRY(EGLBoolean, eglQueryStreamTimeKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLTimeKHR*)
+EGL_ENTRY(EGLSurface, eglCreateStreamProducerSurfaceKHR, EGLDisplay, EGLConfig, EGLStreamKHR, const EGLint*)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerGLTextureExternalKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerAcquireKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLBoolean, eglStreamConsumerReleaseKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLNativeFileDescriptorKHR, eglGetStreamFileDescriptorKHR, EGLDisplay, EGLStreamKHR)
+EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR, EGLDisplay, EGLNativeFileDescriptorKHR)
+EGL_ENTRY(EGLint, eglWaitSync, EGLDisplay, EGLSync, EGLint)
+EGL_ENTRY(EGLint, eglWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint)
+EGL_ENTRY(EGLint, eglDupNativeFenceFDANDROID, EGLDisplay, EGLSyncKHR)
+EGL_ENTRY(EGLBoolean, eglPresentationTimeANDROID, EGLDisplay, EGLSurface, EGLnsecsANDROID)
+EGL_ENTRY(EGLClientBuffer, eglGetNativeClientBufferANDROID, const AHardwareBuffer*)
+EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void)
+EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void)
+EGL_ENTRY(EGLBoolean, eglSetDamageRegionKHR, EGLDisplay, EGLSurface, EGLint*, EGLint)
+EGL_ENTRY(EGLBoolean, eglGetNextFrameIdANDROID, EGLDisplay, EGLSurface, EGLuint64KHR*)
+EGL_ENTRY(EGLBoolean, eglGetCompositorTimingANDROID, EGLDisplay, EGLSurface, EGLint, const EGLint*, EGLnsecsANDROID*)
+EGL_ENTRY(EGLBoolean, eglGetCompositorTimingSupportedANDROID, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglGetFrameTimestampsANDROID, EGLDisplay, EGLSurface, EGLuint64KHR, EGLint, const EGLint*, EGLnsecsANDROID*)
+EGL_ENTRY(EGLBoolean, eglGetFrameTimestampSupportedANDROID, EGLDisplay, EGLSurface, EGLint)
+GL_ENTRY(const GLubyte*, glGetString, GLenum)
+GL_ENTRY(const GLubyte*, glGetStringi, GLenum, GLuint)
+GL_ENTRY(void, glGetBooleanv, GLenum, GLboolean*)
+GL_ENTRY(void, glGetFloatv, GLenum, GLfloat*)
+GL_ENTRY(void, glGetIntegerv, GLenum, GLint*)
+GL_ENTRY(void, glGetInteger64v, GLenum, GLint64*)
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index f9e96ea..9fa58e2 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -93,6 +93,7 @@
pushd out > /dev/null
mkdir classes
javac -d classes android/opengl/EGL14.java \
+ android/opengl/EGL15.java \
android/opengl/EGLExt.java \
com/google/android/gles_jni/GLImpl.java \
javax/microedition/khronos/opengles/GL10.java \
@@ -155,13 +156,13 @@
compareGenerated ../../../../base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x
done
-for x in EGL14 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32
+for x in EGL14 EGL15 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32
do
compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
compareGenerated ../../../../base/core/jni generated/C android_opengl_${x}.cpp
done
-for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface
+for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface EGLImage EGLSync
do
compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java
done
diff --git a/opengl/tools/glgen/specs/egl/EGL15.spec b/opengl/tools/glgen/specs/egl/EGL15.spec
new file mode 100644
index 0000000..e0aad30
--- /dev/null
+++ b/opengl/tools/glgen/specs/egl/EGL15.spec
@@ -0,0 +1,14 @@
+EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list )
+EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync )
+EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout )
+EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value )
+// NOTE: native_display isn't actually an EGLAttrib. Using EGLAttrib
+// so that the generate creates mostly correct code (do not want a buffer)
+// have to manually change cast to (void *) in generated code that calls
+// the native function.
+EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list )
+EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list )
+EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags )
+EGLImage eglCreateImage ( EGLDisplay dpy, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list )
+EGLBoolean eglDestroyImage ( EGLDisplay dpy, EGLImage image )
diff --git a/opengl/tools/glgen/specs/egl/checks.spec b/opengl/tools/glgen/specs/egl/checks.spec
index ae531ee..e2bae48 100644
--- a/opengl/tools/glgen/specs/egl/checks.spec
+++ b/opengl/tools/glgen/specs/egl/checks.spec
@@ -11,3 +11,5 @@
//STUB function: eglCreatePbufferFromClientBuffer nullAllowed attrib_list sentinel attrib_list EGL_NONE
eglCreateContext sentinel attrib_list EGL_NONE
eglQueryContext check value 1
+//unsupported: eglCreatePlatformPixmapSurface nullAllowed attrib_list sentinel attrib_list EGL_NONE
+eglCreatePlatformPixmapSurface unsupported
diff --git a/opengl/tools/glgen/src/CType.java b/opengl/tools/glgen/src/CType.java
index aba98af..b1f8e05 100644
--- a/opengl/tools/glgen/src/CType.java
+++ b/opengl/tools/glgen/src/CType.java
@@ -57,7 +57,9 @@
if(baseType.equals("EGLContext")
|| baseType.equals("EGLConfig")
|| baseType.equals("EGLSurface")
- || baseType.equals("EGLDisplay")) {
+ || baseType.equals("EGLDisplay")
+ || baseType.equals("EGLImage")
+ || baseType.equals("EGLSync")) {
return true;
}
return false;
diff --git a/opengl/tools/glgen/src/GenerateEGL.java b/opengl/tools/glgen/src/GenerateEGL.java
index 2ef3970..57958c6 100644
--- a/opengl/tools/glgen/src/GenerateEGL.java
+++ b/opengl/tools/glgen/src/GenerateEGL.java
@@ -84,7 +84,7 @@
ParameterChecker checker = new ParameterChecker(checksReader);
- for(String suffix: new String[] {"EGL14", "EGLExt"}) {
+ for(String suffix: new String[] {"EGL14", "EGL15", "EGLExt"}) {
BufferedReader specReader = new BufferedReader(new FileReader(
"specs/egl/" + suffix + ".spec"));
String egljFilename = "android/opengl/" + suffix + ".java";
diff --git a/opengl/tools/glgen/src/JType.java b/opengl/tools/glgen/src/JType.java
index 7f08503..0b4401a 100644
--- a/opengl/tools/glgen/src/JType.java
+++ b/opengl/tools/glgen/src/JType.java
@@ -60,12 +60,16 @@
typeMapping.put(new CType("EGLNativeDisplayType"), new JType("long"));
typeMapping.put(new CType("EGLClientBuffer"), new JType("long"));
typeMapping.put(new CType("EGLnsecsANDROID"), new JType("long"));
+ typeMapping.put(new CType("EGLAttrib"), new JType("long"));
+ typeMapping.put(new CType("EGLTime"), new JType("long"));
// EGL nonprimitive types
typeMapping.put(new CType("EGLConfig"), new JType("EGLConfig", true, false));
typeMapping.put(new CType("EGLContext"), new JType("EGLContext", true, false));
typeMapping.put(new CType("EGLDisplay"), new JType("EGLDisplay", true, false));
typeMapping.put(new CType("EGLSurface"), new JType("EGLSurface", true, false));
+ typeMapping.put(new CType("EGLImage"), new JType("EGLImage", true, false));
+ typeMapping.put(new CType("EGLSync"), new JType("EGLSync", true, false));
// Untyped pointers map to untyped Buffers
@@ -139,6 +143,8 @@
arrayTypeMapping.put(new CType("EGLint", true, true), new JType("int", false, true));
arrayTypeMapping.put(new CType("EGLConfig", false, true), new JType("EGLConfig", true, true));
arrayTypeMapping.put(new CType("EGLConfig", true, true), new JType("EGLConfig", true, true));
+ arrayTypeMapping.put(new CType("EGLAttrib", false, true), new JType("long", false, true));
+ arrayTypeMapping.put(new CType("EGLAttrib", true, true), new JType("long", false, true));
}
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index e8691bb..6697189 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -103,6 +103,12 @@
if (cfunc.hasEGLHandleArg()) {
return;
}
+ // eglGetPlatformDisplay does not have any EGLHandleArgs
+ // but we do not want to create IOBuffers of this, so
+ // exit
+ if (cfunc.getName().equals("eglGetPlatformDisplay")) {
+ return;
+ }
}
jfunc = JFunc.convert(cfunc, false);
diff --git a/opengl/tools/glgen/static/egl/EGLImage.java b/opengl/tools/glgen/static/egl/EGLImage.java
new file mode 100644
index 0000000..731ce72
--- /dev/null
+++ b/opengl/tools/glgen/static/egl/EGLImage.java
@@ -0,0 +1,37 @@
+/*
+**
+** 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.
+*/
+
+package android.opengl;
+
+/**
+ * Wrapper class for native EGLImage objects.
+ *
+ */
+public class EGLImage extends EGLObjectHandle {
+ private EGLImage(long handle) {
+ super(handle);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof EGLImage)) return false;
+
+ EGLImage that = (EGLImage) o;
+ return getNativeHandle() == that.getNativeHandle();
+ }
+}
diff --git a/opengl/tools/glgen/static/egl/EGLSync.java b/opengl/tools/glgen/static/egl/EGLSync.java
new file mode 100644
index 0000000..472f9e7
--- /dev/null
+++ b/opengl/tools/glgen/static/egl/EGLSync.java
@@ -0,0 +1,37 @@
+/*
+**
+** 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.
+*/
+
+package android.opengl;
+
+/**
+ * Wrapper class for native EGLSync objects.
+ *
+ */
+public class EGLSync extends EGLObjectHandle {
+ private EGLSync(long handle) {
+ super(handle);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof EGLSync)) return false;
+
+ EGLSync that = (EGLSync) o;
+ return getNativeHandle() == that.getNativeHandle();
+ }
+}
diff --git a/opengl/tools/glgen/stubs/egl/EGL15Header.java-if b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if
new file mode 100644
index 0000000..859380f
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if
@@ -0,0 +1,78 @@
+/*
+** 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.
+*/
+
+package android.opengl;
+
+/**
+ * EGL 1.5
+ *
+ */
+public final class EGL15 {
+
+ private EGL15() {};
+
+ public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT = 0x00000001;
+ public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT = 0x00000002;
+ public static final int EGL_OPENGL_ES3_BIT = 0x00000040;
+ public static final int EGL_SYNC_FLUSH_COMMANDS_BIT = 0x0001;
+ public static final int EGL_GL_COLORSPACE_SRGB = 0x3089;
+ public static final int EGL_GL_COLORSPACE_LINEAR = 0x308A;
+ public static final int EGL_CONTEXT_MAJOR_VERSION = 0x3098;
+ public static final int EGL_CL_EVENT_HANDLE = 0x309C;
+ public static final int EGL_GL_COLORSPACE = 0x309D;
+ public static final int EGL_GL_TEXTURE_2D = 0x30B1;
+ public static final int EGL_GL_TEXTURE_3D = 0x30B2;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x30B3;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x30B4;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x30B5;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x30B6;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x30B7;
+ public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x30B8;
+ public static final int EGL_GL_RENDERBUFFER = 0x30B9;
+ public static final int EGL_GL_TEXTURE_LEVEL = 0x30BC;
+ public static final int EGL_GL_TEXTURE_ZOFFSET = 0x30BD;
+ public static final int EGL_IMAGE_PRESERVED = 0x30D2;
+ public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE = 0x30F0;
+ public static final int EGL_SYNC_STATUS = 0x30F1;
+ public static final int EGL_SIGNALED = 0x30F2;
+ public static final int EGL_UNSIGNALED = 0x30F3;
+ public static final int EGL_TIMEOUT_EXPIRED = 0x30F5;
+ public static final int EGL_CONDITION_SATISFIED = 0x30F6;
+ public static final int EGL_SYNC_TYPE = 0x30F7;
+ public static final int EGL_SYNC_CONDITION = 0x30F8;
+ public static final int EGL_SYNC_FENCE = 0x30F9;
+ public static final int EGL_CONTEXT_MINOR_VERSION = 0x30FB;
+ public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK = 0x30FD;
+ public static final int EGL_SYNC_CL_EVENT = 0x30FE;
+ public static final int EGL_SYNC_CL_EVENT_COMPLETE = 0x30FF;
+ public static final int EGL_CONTEXT_OPENGL_DEBUG = 0x31B0;
+ public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE = 0x31B1;
+ public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS = 0x31B2;
+ public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 0x31BD;
+ public static final int EGL_NO_RESET_NOTIFICATION = 0x31BE;
+ public static final int EGL_LOSE_CONTEXT_ON_RESET = 0x31BF;
+ public static final int EGL_PLATFORM_ANDROID_KHR = 0x3141;
+ public static final long EGL_FOREVER = 0xFFFFFFFFFFFFFFFFL;
+ public static final EGLImage EGL_NO_IMAGE = null;
+ public static final EGLSync EGL_NO_SYNC = null;
+ public static final EGLContext EGL_NO_CONTEXT = null;
+ public static final EGLDisplay EGL_NO_DISPLAY = null;
+ public static final EGLSurface EGL_NO_SURFACE = null;
+
+ native private static void _nativeClassInit();
+ static {
+ _nativeClassInit();
+ }
diff --git a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
new file mode 100644
index 0000000..70b46f7
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp
@@ -0,0 +1,205 @@
+/*
+** 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 GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/misc.h>
+#include "jni.h"
+
+#include <EGL/egl.h>
+#include <assert.h>
+
+#include <ui/ANativeObjectBase.h>
+
+static int initialized = 0;
+
+// classes from EGL 1.4
+static jclass egldisplayClass;
+static jclass eglsurfaceClass;
+static jclass eglconfigClass;
+static jclass eglcontextClass;
+static jclass bufferClass;
+static jclass nioAccessClass;
+
+static jfieldID positionID;
+static jfieldID limitID;
+static jfieldID elementSizeShiftID;
+
+static jmethodID getBasePointerID;
+static jmethodID getBaseArrayID;
+static jmethodID getBaseArrayOffsetID;
+
+static jmethodID egldisplayGetHandleID;
+static jmethodID eglconfigGetHandleID;
+static jmethodID eglcontextGetHandleID;
+static jmethodID eglsurfaceGetHandleID;
+
+static jmethodID egldisplayConstructor;
+static jmethodID eglcontextConstructor;
+static jmethodID eglsurfaceConstructor;
+static jmethodID eglconfigConstructor;
+
+static jobject eglNoContextObject;
+static jobject eglNoDisplayObject;
+static jobject eglNoSurfaceObject;
+
+// classes from EGL 1.5
+static jclass eglimageClass;
+static jclass eglsyncClass;
+
+static jmethodID eglimageGetHandleID;
+static jmethodID eglsyncGetHandleID;
+
+static jmethodID eglimageConstructor;
+static jmethodID eglsyncConstructor;
+
+static jobject eglNoImageObject;
+static jobject eglNoSyncObject;
+
+/* Cache method IDs each time the class is loaded. */
+
+static void nativeClassInit(JNIEnv *_env, jclass glImplClass) {
+ // EGL 1.4 Init
+ jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig");
+ eglconfigClass = (jclass)_env->NewGlobalRef(eglconfigClassLocal);
+ jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext");
+ eglcontextClass = (jclass)_env->NewGlobalRef(eglcontextClassLocal);
+ jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay");
+ egldisplayClass = (jclass)_env->NewGlobalRef(egldisplayClassLocal);
+ jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface");
+ eglsurfaceClass = (jclass)_env->NewGlobalRef(eglsurfaceClassLocal);
+
+ eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J");
+ eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J");
+ egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
+ eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
+
+ eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V");
+ eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V");
+ egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V");
+ eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V");
+
+ jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor,
+ reinterpret_cast<jlong>(EGL_NO_CONTEXT));
+ eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject);
+ jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor,
+ reinterpret_cast<jlong>(EGL_NO_DISPLAY));
+ eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject);
+ jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor,
+ reinterpret_cast<jlong>(EGL_NO_SURFACE));
+ eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject);
+
+ jclass eglClass = _env->FindClass("android/opengl/EGL15");
+ jfieldID noContextFieldID =
+ _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;");
+ _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject);
+
+ jfieldID noDisplayFieldID =
+ _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;");
+ _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject);
+
+ jfieldID noSurfaceFieldID =
+ _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;");
+ _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject);
+
+ // EGL 1.5 init
+ jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
+ nioAccessClass = (jclass)_env->NewGlobalRef(nioAccessClassLocal);
+
+ jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
+ bufferClass = (jclass)_env->NewGlobalRef(bufferClassLocal);
+
+ getBasePointerID =
+ _env->GetStaticMethodID(nioAccessClass, "getBasePointer", "(Ljava/nio/Buffer;)J");
+ getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, "getBaseArray",
+ "(Ljava/nio/Buffer;)Ljava/lang/Object;");
+ getBaseArrayOffsetID =
+ _env->GetStaticMethodID(nioAccessClass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
+
+ positionID = _env->GetFieldID(bufferClass, "position", "I");
+ limitID = _env->GetFieldID(bufferClass, "limit", "I");
+ elementSizeShiftID = _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
+
+ jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage");
+ eglimageClass = (jclass)_env->NewGlobalRef(eglimageClassLocal);
+ jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync");
+ eglsyncClass = (jclass)_env->NewGlobalRef(eglsyncClassLocal);
+
+ eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+ eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+ eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V");
+ eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V");
+
+ jfieldID noImageFieldID =
+ _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;");
+ _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject);
+
+ jfieldID noSyncFieldID =
+ _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;");
+ _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject);
+}
+
+static void *getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining,
+ jint *offset) {
+ jint position;
+ jint limit;
+ jint elementSizeShift;
+ jlong pointer;
+
+ position = _env->GetIntField(buffer, positionID);
+ limit = _env->GetIntField(buffer, limitID);
+ elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+ *remaining = (limit - position) << elementSizeShift;
+ pointer = _env->CallStaticLongMethod(nioAccessClass, getBasePointerID, buffer);
+ if (pointer != 0L) {
+ *array = NULL;
+ return reinterpret_cast<void *>(pointer);
+ }
+ eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+ eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+ *array = (jarray)_env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer);
+ *offset = _env->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetID, buffer);
+
+ return NULL;
+}
+
+static void releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) {
+ _env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT);
+}
+
+static void *fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) {
+ if (obj == NULL) {
+ jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null.");
+ }
+
+ jlong handle = _env->CallLongMethod(obj, mid);
+ return reinterpret_cast<void *>(handle);
+}
+
+static jobject toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void *handle) {
+ if (cls == eglimageClass && (EGLImage)handle == EGL_NO_IMAGE) {
+ return eglNoImageObject;
+ }
+
+ return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle));
+}
+
+// --------------------------------------------------------------------------
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp
new file mode 100644
index 0000000..fd44498
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp
@@ -0,0 +1,46 @@
+/* EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglGetPlatformDisplay
+ (JNIEnv *_env, jobject _this, jint platform, jlong native_display, jlongArray attrib_list_ref, jint offset) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ EGLDisplay _returnValue = (EGLDisplay) 0;
+ EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+ jint _remaining;
+ EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+ if (!attrib_list_ref) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "attrib_list == null";
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "offset < 0";
+ goto exit;
+ }
+ _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+ attrib_list_base = (EGLAttrib *)
+ _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+ attrib_list = attrib_list_base + offset;
+
+ _returnValue = eglGetPlatformDisplay(
+ (EGLenum)platform,
+ (void *)native_display,
+ (EGLAttrib *)attrib_list
+ );
+
+exit:
+ if (attrib_list_base) {
+ _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+ JNI_ABORT);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue);
+}
+
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java
new file mode 100644
index 0000000..28945e8
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java
@@ -0,0 +1,9 @@
+ // C function EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+
+ public static native EGLDisplay eglGetPlatformDisplay(
+ int platform,
+ long native_display,
+ long[] attrib_list,
+ int offset
+ );
+
diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg
new file mode 100644
index 0000000..8a309bf
--- /dev/null
+++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg
@@ -0,0 +1 @@
+{"eglGetPlatformDisplay", "(IJ[JI)Landroid/opengl/EGLDisplay;", (void *) android_eglGetPlatformDisplay },
diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml
index e422e96..26771e1 100644
--- a/opengl/tools/glgen2/registry/egl.xml
+++ b/opengl/tools/glgen2/registry/egl.xml
@@ -801,7 +801,9 @@
<enum value="0x3361" name="EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT"/>
<enum value="0x3362" name="EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT"/>
<enum value="0x3363" name="EGL_GL_COLORSPACE_DISPLAY_P3_EXT"/>
- <unused start="0x3364" end="0x339F"/>
+ <enum value="0x3364" name="EGL_SYNC_CLIENT_EXT"/>
+ <enum value="0x3365" name="EGL_SYNC_CLIENT_SIGNAL_EXT"/>
+ <unused start="0x3366" end="0x339F"/>
</enums>
<enums namespace="EGL" start="0x33A0" end="0x33AF" vendor="ANGLE" comment="Reserved for Shannon Woods (Bug 13175)">
@@ -887,6 +889,10 @@
<enum value="0x3471" name="EGL_IMPORT_IMPLICIT_SYNC_EXT"/>
<enum value="0x3472" name="EGL_IMPORT_EXPLICIT_SYNC_EXT"/>
</enums>
+ <enums namespace="EGL" start="0x3480" end="0x348F" vendor="ANGLE" comment="Reserved for Courtney Goeltzenleuchter - ANGLE (gitlab EGL bug 7)">
+ <enum value="0x3480" name="EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE"/>
+ <unused start="0x3481" end="0x348F"/>
+ </enums>
<!-- Please remember that new enumerant allocations must be obtained by
request to the Khronos API registrar (see comments at the top of this
@@ -897,8 +903,8 @@
<!-- Reservable for future use. To generate a new range, allocate multiples
of 16 starting at the lowest available point in this block. -->
- <enums namespace="EGL" start="0x3480" end="0x3FFF" vendor="KHR" comment="Reserved for future use">
- <unused start="0x3480" end="0x3FFF"/>
+ <enums namespace="EGL" start="0x3490" end="0x3FFF" vendor="KHR" comment="Reserved for future use">
+ <unused start="0x3490" end="0x3FFF"/>
</enums>
<enums namespace="EGL" start="0x8F70" end="0x8F7F" vendor="HI" comment="For Mark Callow, Khronos bug 4055. Shared with GL.">
@@ -930,6 +936,12 @@
<param><ptype>EGLint</ptype> *<name>num_config</name></param>
</command>
<command>
+ <proto><ptype>EGLBoolean</ptype> <name>eglClientSignalSyncEXT</name></proto>
+ <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+ <param><ptype>EGLSync</ptype> <name>sync</name></param>
+ <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
+ </command>
+ <command>
<proto><ptype>EGLint</ptype> <name>eglClientWaitSync</name></proto>
<param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
<param><ptype>EGLSync</ptype> <name>sync</name></param>
@@ -1647,6 +1659,11 @@
<param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
</command>
<command>
+ <proto><ptype>EGLBoolean</ptype> <name>eglStreamFlushNV</name></proto>
+ <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+ <param><ptype>EGLStreamKHR</ptype> <name>stream</name></param>
+ </command>
+ <command>
<proto><ptype>EGLBoolean</ptype> <name>eglSurfaceAttrib</name></proto>
<param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
<param><ptype>EGLSurface</ptype> <name>surface</name></param>
@@ -1701,6 +1718,12 @@
<param><ptype>EGLSurface</ptype> <name>surface</name></param>
</command>
<command>
+ <proto><ptype>EGLBoolean</ptype> <name>eglUnsignalSyncEXT</name></proto>
+ <param><ptype>EGLDisplay</ptype> <name>dpy</name></param>
+ <param><ptype>EGLSync</ptype> <name>sync</name></param>
+ <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param>
+ </command>
+ <command>
<proto><ptype>EGLBoolean</ptype> <name>eglWaitClient</name></proto>
</command>
<command>
@@ -2146,6 +2169,13 @@
</require>
</extension>
<extension name="EGL_EXT_client_extensions" supported="egl"/>
+ <extension name="EGL_EXT_client_sync" supported="egl">
+ <require>
+ <enum name="EGL_SYNC_CLIENT_EXT"/>
+ <enum name="EGL_SYNC_CLIENT_SIGNAL_EXT"/>
+ <command name="eglClientSignalSyncEXT"/>
+ </require>
+ </extension>
<extension name="EGL_EXT_create_context_robustness" supported="egl">
<require>
<enum name="EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT"/>
@@ -2371,6 +2401,11 @@
<command name="eglSwapBuffersWithDamageEXT"/>
</require>
</extension>
+ <extension name="EGL_EXT_sync_reuse" supported="egl">
+ <require>
+ <command name="eglUnsignalSyncEXT"/>
+ </require>
+ </extension>
<extension name="EGL_EXT_yuv_surface" supported="egl">
<require>
<enum name="EGL_YUV_ORDER_EXT"/>
@@ -2932,6 +2967,11 @@
<enum name="EGL_STREAM_FIFO_SYNCHRONOUS_NV"/>
</require>
</extension>
+ <extension name="EGL_NV_stream_flush" supported="egl">
+ <require>
+ <command name="eglStreamFlushNV"/>
+ </require>
+ </extension>
<extension name="EGL_NV_stream_frame_limits" supported="egl">
<require>
<enum name="EGL_PRODUCER_MAX_FRAME_HINT_NV"/>
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 9a65452..622a623 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -41,6 +41,8 @@
"-Wall",
"-Wextra",
"-Werror",
+ // Allow implicit fallthroughs in InputReader.cpp until they are fixed.
+ "-Wno-error=implicit-fallthrough",
"-Wno-unused-parameter",
// TODO: Move inputflinger to its own process and mark it hidden
//-fvisibility=hidden
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 38104c4..2cad986 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -852,7 +852,8 @@
return true;
}
- addMonitoringTargetsLocked(inputTargets);
+ // Add monitor channels from event's or focused display.
+ addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);
@@ -919,7 +920,8 @@
return true;
}
- addMonitoringTargetsLocked(inputTargets);
+ // Add monitor channels from event's or focused display.
+ addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
// Dispatch the motion.
if (conflictingPointerActions) {
@@ -1665,17 +1667,29 @@
target.pointerIds = pointerIds;
}
-void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets) {
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- inputTargets.push();
+void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets,
+ int32_t displayId) {
+ std::unordered_map<int32_t, Vector<sp<InputChannel>>>::const_iterator it =
+ mMonitoringChannelsByDisplay.find(displayId);
- InputTarget& target = inputTargets.editTop();
- target.inputChannel = mMonitoringChannels[i];
- target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.xOffset = 0;
- target.yOffset = 0;
- target.pointerIds.clear();
- target.scaleFactor = 1.0f;
+ if (it != mMonitoringChannelsByDisplay.end()) {
+ const Vector<sp<InputChannel>>& monitoringChannels = it->second;
+ const size_t numChannels = monitoringChannels.size();
+ for (size_t i = 0; i < numChannels; i++) {
+ inputTargets.push();
+
+ InputTarget& target = inputTargets.editTop();
+ target.inputChannel = monitoringChannels[i];
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ target.xOffset = 0;
+ target.yOffset = 0;
+ target.pointerIds.clear();
+ target.scaleFactor = 1.0f;
+ }
+ } else {
+ // If there is no monitor channel registered or all monitor channel unregistered,
+ // the display can't detect the extra system gesture by a copy of input events.
+ ALOGW("There is no monitor channel found in display=%" PRId32, displayId);
}
}
@@ -2294,8 +2308,12 @@
void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
const CancelationOptions& options) {
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- synthesizeCancelationEventsForInputChannelLocked(mMonitoringChannels[i], options);
+ for (auto& it : mMonitoringChannelsByDisplay) {
+ const Vector<sp<InputChannel>>& monitoringChannels = it.second;
+ const size_t numChannels = monitoringChannels.size();
+ for (size_t i = 0; i < numChannels; i++) {
+ synthesizeCancelationEventsForInputChannelLocked(monitoringChannels[i], options);
+ }
}
}
@@ -3486,12 +3504,16 @@
dump += INDENT "Displays: <none>\n";
}
- if (!mMonitoringChannels.isEmpty()) {
- dump += INDENT "MonitoringChannels:\n";
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- const sp<InputChannel>& channel = mMonitoringChannels[i];
- dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str());
- }
+ if (!mMonitoringChannelsByDisplay.empty()) {
+ for (auto& it : mMonitoringChannelsByDisplay) {
+ const Vector<sp<InputChannel>>& monitoringChannels = it.second;
+ dump += INDENT "MonitoringChannels in Display %d:\n";
+ const size_t numChannels = monitoringChannels.size();
+ for (size_t i = 0; i < numChannels; i++) {
+ const sp<InputChannel>& channel = monitoringChannels[i];
+ dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str());
+ }
+ }
} else {
dump += INDENT "MonitoringChannels: <none>\n";
}
@@ -3609,10 +3631,10 @@
}
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
+ const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId) {
#if DEBUG_REGISTRATION
- ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().c_str(),
- toString(monitor));
+ ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32,
+ inputChannel->getName().c_str(), displayId);
#endif
{ // acquire lock
@@ -3624,13 +3646,20 @@
return BAD_VALUE;
}
+ // If InputWindowHandle is null and displayId is not ADISPLAY_ID_NONE,
+ // treat inputChannel as monitor channel for displayId.
+ bool monitor = inputWindowHandle == nullptr && displayId != ADISPLAY_ID_NONE;
+
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
+ // Store monitor channel by displayId.
if (monitor) {
- mMonitoringChannels.push(inputChannel);
+ Vector<sp<InputChannel>>& monitoringChannels =
+ mMonitoringChannelsByDisplay[displayId];
+ monitoringChannels.push(inputChannel);
}
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
@@ -3687,11 +3716,21 @@
}
void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) {
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- if (mMonitoringChannels[i] == inputChannel) {
- mMonitoringChannels.removeAt(i);
- break;
- }
+ for (auto it = mMonitoringChannelsByDisplay.begin();
+ it != mMonitoringChannelsByDisplay.end(); ) {
+ Vector<sp<InputChannel>>& monitoringChannels = it->second;
+ const size_t numChannels = monitoringChannels.size();
+ for (size_t i = 0; i < numChannels; i++) {
+ if (monitoringChannels[i] == inputChannel) {
+ monitoringChannels.removeAt(i);
+ break;
+ }
+ }
+ if (monitoringChannels.empty()) {
+ it = mMonitoringChannelsByDisplay.erase(it);
+ } else {
+ ++it;
+ }
}
}
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index aedad2f..5efb2fa 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -346,13 +346,19 @@
virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
const sp<InputChannel>& toChannel) = 0;
- /* Registers or unregister input channels that may be used as targets for input events.
- * If monitor is true, the channel will receive a copy of all input events.
+ /* Registers input channels that may be used as targets for input events.
+ * If inputWindowHandle is null, and displayId is not ADISPLAY_ID_NONE,
+ * the channel will receive a copy of all input events form the specific displayId.
*
- * These methods may be called on any thread (usually by the input manager).
+ * This method may be called on any thread (usually by the input manager).
*/
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor) = 0;
+ const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId) = 0;
+
+ /* Unregister input channels that will no longer receive input events.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
};
@@ -407,7 +413,7 @@
const sp<InputChannel>& toChannel);
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
- const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
+ const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId);
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
private:
@@ -914,8 +920,8 @@
ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
- // Input channels that will receive a copy of all input events.
- Vector<sp<InputChannel> > mMonitoringChannels;
+ // Input channels that will receive a copy of all input events sent to the provided display.
+ std::unordered_map<int32_t, Vector<sp<InputChannel>>> mMonitoringChannelsByDisplay;
// Event injection and synchronization.
Condition mInjectionResultAvailableCondition;
@@ -1070,7 +1076,7 @@
void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
- void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets);
+ void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId);
void pokeUserActivityLocked(const EventEntry* eventEntry);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f75b0b6..c6eaf9f 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -278,68 +278,8 @@
}
};
-class FakeWindowHandle : public InputWindowHandle {
+class FakeInputReceiver {
public:
- static const int32_t WIDTH = 600;
- static const int32_t HEIGHT = 800;
-
- FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<InputDispatcher>& dispatcher, const std::string name) :
- InputWindowHandle(inputApplicationHandle), mDispatcher(dispatcher),
- mName(name), mFocused(false), mDisplayId(ADISPLAY_ID_DEFAULT) {
- InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
- mConsumer = new InputConsumer(mClientChannel);
- mDispatcher->registerInputChannel(mServerChannel, this, false);
- }
-
- virtual ~FakeWindowHandle() {
- mDispatcher->unregisterInputChannel(mServerChannel);
- mServerChannel.clear();
- mClientChannel.clear();
- mDispatcher.clear();
-
- if (mConsumer != nullptr) {
- delete mConsumer;
- }
- }
-
- virtual bool updateInfo() {
- if (!mInfo) {
- mInfo = new InputWindowInfo();
- }
- mInfo->inputChannel = mServerChannel;
- mInfo->name = mName;
- mInfo->layoutParamsFlags = 0;
- mInfo->layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
- mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
- mInfo->frameLeft = 0;
- mInfo->frameTop = 0;
- mInfo->frameRight = WIDTH;
- mInfo->frameBottom = HEIGHT;
- mInfo->scaleFactor = 1.0;
- mInfo->addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
- mInfo->visible = true;
- mInfo->canReceiveKeys = true;
- mInfo->hasFocus = mFocused;
- mInfo->hasWallpaper = false;
- mInfo->paused = false;
- mInfo->layer = 0;
- mInfo->ownerPid = INJECTOR_PID;
- mInfo->ownerUid = INJECTOR_UID;
- mInfo->inputFeatures = 0;
- mInfo->displayId = mDisplayId;
-
- return true;
- }
-
- void setFocus() {
- mFocused = true;
- }
-
- void setDisplayId(int32_t displayId) {
- mDisplayId = displayId;
- }
-
void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId,
int32_t expectedFlags = 0) {
uint32_t consumeSeq;
@@ -376,7 +316,7 @@
ASSERT_EQ(expectedFlags, flags)
<< mName.c_str() << ": event flags should be the same as expected.";
- status = mConsumer->sendFinishedSignal(consumeSeq, true /*handled*/);
+ status = mConsumer->sendFinishedSignal(consumeSeq, handled());
ASSERT_EQ(OK, status)
<< mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
@@ -391,17 +331,94 @@
<< ": should not have received any events, so consume(..) should not return OK.";
}
- private:
+protected:
+ explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher,
+ const std::string name, int32_t displayId) :
+ mDispatcher(dispatcher), mName(name), mDisplayId(displayId) {
+ InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+ mConsumer = new InputConsumer(mClientChannel);
+ }
+
+ virtual ~FakeInputReceiver() {
+ }
+
+ // return true if the event has been handled.
+ virtual bool handled() {
+ return false;
+ }
+
sp<InputDispatcher> mDispatcher;
sp<InputChannel> mServerChannel, mClientChannel;
InputConsumer *mConsumer;
PreallocatedInputEventFactory mEventFactory;
std::string mName;
- bool mFocused;
int32_t mDisplayId;
};
+class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
+public:
+ static const int32_t WIDTH = 600;
+ static const int32_t HEIGHT = 800;
+
+ FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
+ InputWindowHandle(inputApplicationHandle),
+ FakeInputReceiver(dispatcher, name, displayId),
+ mFocused(false) {
+ mDispatcher->registerInputChannel(mServerChannel, this, displayId);
+ }
+
+ virtual bool updateInfo() {
+ if (!mInfo) {
+ mInfo = new InputWindowInfo();
+ }
+ mInfo->inputChannel = mServerChannel;
+ mInfo->name = mName;
+ mInfo->layoutParamsFlags = 0;
+ mInfo->layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+ mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo->frameLeft = 0;
+ mInfo->frameTop = 0;
+ mInfo->frameRight = WIDTH;
+ mInfo->frameBottom = HEIGHT;
+ mInfo->scaleFactor = 1.0;
+ mInfo->addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+ mInfo->visible = true;
+ mInfo->canReceiveKeys = true;
+ mInfo->hasFocus = mFocused;
+ mInfo->hasWallpaper = false;
+ mInfo->paused = false;
+ mInfo->layer = 0;
+ mInfo->ownerPid = INJECTOR_PID;
+ mInfo->ownerUid = INJECTOR_UID;
+ mInfo->inputFeatures = 0;
+ mInfo->displayId = mDisplayId;
+
+ return true;
+ }
+
+ void setFocus() {
+ mFocused = true;
+ }
+
+ void assertNoEvents() {
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
+ &consumeSeq, &event);
+ ASSERT_NE(OK, status)
+ << mName.c_str()
+ << ": should not have received any events, so consume(..) should not return OK.";
+ }
+protected:
+ virtual bool handled() {
+ return true;
+ }
+
+ bool mFocused;
+};
+
static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
int32_t displayId = ADISPLAY_ID_NONE) {
KeyEvent event;
@@ -419,7 +436,8 @@
INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
}
-static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t displayId) {
+static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
+ int32_t displayId) {
MotionEvent event;
PointerProperties pointerProperties[1];
PointerCoords pointerCoords[1];
@@ -434,7 +452,7 @@
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion down event.
- event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, displayId,
+ event.initialize(DEVICE_ID, source, displayId,
AMOTION_EVENT_ACTION_DOWN, /* actionButton */0, /* flags */ 0, /* edgeFlags */ 0,
AMETA_NONE, /* buttonState */ 0, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
/* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties,
@@ -449,13 +467,15 @@
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
- sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window");
+ sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
+ ADISPLAY_ID_DEFAULT);
Vector<sp<InputWindowHandle>> inputWindowHandles;
inputWindowHandles.add(window);
mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
// Window should receive motion event.
@@ -465,15 +485,18 @@
// The foreground window should receive the first touch down event.
TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
- sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top");
- sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second");
+ sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+ ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+ ADISPLAY_ID_DEFAULT);
Vector<sp<InputWindowHandle>> inputWindowHandles;
inputWindowHandles.add(windowTop);
inputWindowHandles.add(windowSecond);
mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
// Top window should receive the touch down event. Second window should not receive anything.
@@ -483,8 +506,10 @@
TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
- sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top");
- sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second");
+ sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+ ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+ ADISPLAY_ID_DEFAULT);
// Set focus application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -504,61 +529,70 @@
windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
}
-TEST_F(InputDispatcherTest, SetInputWindow_MultiDisplayTouch) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
- sp<FakeWindowHandle> windowInPrimary = new FakeWindowHandle(application, mDispatcher, "D_1");
- sp<FakeWindowHandle> windowInSecondary = new FakeWindowHandle(application, mDispatcher, "D_2");
+/* Test InputDispatcher for MultiDisplay */
+class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
+public:
+ static constexpr int32_t SECOND_DISPLAY_ID = 1;
+ virtual void SetUp() {
+ InputDispatcherTest::SetUp();
- // Test the primary display touch down.
- Vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push(windowInPrimary);
+ application1 = new FakeApplicationHandle();
+ windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
+ ADISPLAY_ID_DEFAULT);
+ Vector<sp<InputWindowHandle>> inputWindowHandles;
+ inputWindowHandles.push(windowInPrimary);
+ // Set focus window for primary display, but focused display would be second one.
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
+ windowInPrimary->setFocus();
+ mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ application2 = new FakeApplicationHandle();
+ windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
+ SECOND_DISPLAY_ID);
+ // Set focus to second display window.
+ Vector<sp<InputWindowHandle>> inputWindowHandles_Second;
+ inputWindowHandles_Second.push(windowInSecondary);
+ // Set focus display to second one.
+ mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
+ // Set focus window for second display.
+ mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
+ windowInSecondary->setFocus();
+ mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+ }
+
+ virtual void TearDown() {
+ InputDispatcherTest::TearDown();
+
+ application1.clear();
+ windowInPrimary.clear();
+ application2.clear();
+ windowInSecondary.clear();
+ }
+
+protected:
+ sp<FakeApplicationHandle> application1;
+ sp<FakeWindowHandle> windowInPrimary;
+ sp<FakeApplicationHandle> application2;
+ sp<FakeWindowHandle> windowInSecondary;
+};
+
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) {
+ // Test touch down on primary display.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
- // Test the second display touch down.
- constexpr int32_t SECOND_DISPLAY_ID = 1;
- windowInSecondary->setDisplayId(SECOND_DISPLAY_ID);
- Vector<sp<InputWindowHandle>> inputWindowHandles_Second;
- inputWindowHandles_Second.push(windowInSecondary);
-
- mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, SECOND_DISPLAY_ID))
+ // Test touch down on second display.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
}
-TEST_F(InputDispatcherTest, SetInputWindow_FocusedInMultiDisplay) {
- sp<FakeApplicationHandle> application = new FakeApplicationHandle();
- sp<FakeWindowHandle> windowInPrimary = new FakeWindowHandle(application, mDispatcher, "D_1");
- sp<FakeApplicationHandle> application2 = new FakeApplicationHandle();
- sp<FakeWindowHandle> windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2");
-
- constexpr int32_t SECOND_DISPLAY_ID = 1;
-
- // Set focus to primary display window.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- windowInPrimary->setFocus();
-
- // Set focus to second display window.
- mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
- mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
- windowInSecondary->setFocus();
-
- // Update all windows per displays.
- Vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push(windowInPrimary);
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
-
- windowInSecondary->setDisplayId(SECOND_DISPLAY_ID);
- Vector<sp<InputWindowHandle>> inputWindowHandles_Second;
- inputWindowHandles_Second.push(windowInSecondary);
- mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
-
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
// Test inject a key down with display id specified.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -572,8 +606,8 @@
windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
// Remove secondary display.
- inputWindowHandles_Second.clear();
- mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+ Vector<sp<InputWindowHandle>> noWindows;
+ mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID);
// Expect old focus should receive a cancel event.
windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE,
@@ -586,4 +620,66 @@
windowInSecondary->assertNoEvents();
}
+class FakeMonitorReceiver : public FakeInputReceiver, public RefBase {
+public:
+ FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
+ int32_t displayId) : FakeInputReceiver(dispatcher, name, displayId) {
+ mDispatcher->registerInputChannel(mServerChannel, nullptr, displayId);
+ }
+};
+
+// Test per-display input monitors for motion event.
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
+ sp<FakeMonitorReceiver> monitorInPrimary =
+ new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ sp<FakeMonitorReceiver> monitorInSecondary =
+ new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+ // Test touch down on primary display.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+ monitorInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+ windowInSecondary->assertNoEvents();
+ monitorInSecondary->assertNoEvents();
+
+ // Test touch down on second display.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->assertNoEvents();
+ monitorInPrimary->assertNoEvents();
+ windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+ monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+
+ // Test inject a non-pointer motion event.
+ // If specific a display, it will dispatch to the focused window of particular display,
+ // or it will dispatch to the focused window of focused display.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+ AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->assertNoEvents();
+ monitorInPrimary->assertNoEvents();
+ windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
+ monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
+}
+
+// Test per-display input monitors for key event.
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
+ //Input monitor per display.
+ sp<FakeMonitorReceiver> monitorInPrimary =
+ new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ sp<FakeMonitorReceiver> monitorInSecondary =
+ new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+ // Test inject a key down.
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ windowInPrimary->assertNoEvents();
+ monitorInPrimary->assertNoEvents();
+ windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+ monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+}
+
} // namespace android
diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp
index cec2ae5..207b097 100644
--- a/services/sensorservice/RecentEventLogger.cpp
+++ b/services/sensorservice/RecentEventLogger.cpp
@@ -32,19 +32,26 @@
RecentEventLogger::RecentEventLogger(int sensorType) :
mSensorType(sensorType), mEventSize(eventSizeBySensorType(mSensorType)),
- mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false) {
+ mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false),
+ mIsLastEventCurrent(false) {
// blank
}
void RecentEventLogger::addEvent(const sensors_event_t& event) {
std::lock_guard<std::mutex> lk(mLock);
mRecentEvents.emplace(event);
+ mIsLastEventCurrent = true;
}
bool RecentEventLogger::isEmpty() const {
return mRecentEvents.size() == 0;
}
+void RecentEventLogger::setLastEventStale() {
+ std::lock_guard<std::mutex> lk(mLock);
+ mIsLastEventCurrent = false;
+}
+
std::string RecentEventLogger::dump() const {
std::lock_guard<std::mutex> lk(mLock);
@@ -85,10 +92,10 @@
}
}
-bool RecentEventLogger::populateLastEvent(sensors_event_t *event) const {
+bool RecentEventLogger::populateLastEventIfCurrent(sensors_event_t *event) const {
std::lock_guard<std::mutex> lk(mLock);
- if (mRecentEvents.size()) {
+ if (mIsLastEventCurrent && mRecentEvents.size()) {
// Index 0 contains the latest event emplace()'ed
*event = mRecentEvents[0].mEvent;
return true;
diff --git a/services/sensorservice/RecentEventLogger.h b/services/sensorservice/RecentEventLogger.h
index bf1f655..67378b7 100644
--- a/services/sensorservice/RecentEventLogger.h
+++ b/services/sensorservice/RecentEventLogger.h
@@ -37,8 +37,13 @@
public:
explicit RecentEventLogger(int sensorType);
void addEvent(const sensors_event_t& event);
- bool populateLastEvent(sensors_event_t *event) const;
+
+ // Populate event with the last recorded sensor event if it is not stale. An event is
+ // considered stale if the sensor has become deactivated since the event was recorded.
+ // returns true on success, false if no recent event is available or the last event is stale
+ bool populateLastEventIfCurrent(sensors_event_t *event) const;
bool isEmpty() const;
+ void setLastEventStale();
virtual ~RecentEventLogger() {}
// Dumpable interface
@@ -59,6 +64,7 @@
RingBuffer<SensorEventLog> mRecentEvents;
bool mMaskData;
+ bool mIsLastEventCurrent;
private:
static size_t logSizeBySensorType(int sensorType);
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 1b9b945..85450f8 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1290,6 +1290,15 @@
ALOGD_IF(DEBUG_CONNECTIONS, "... and it was the last connection");
mActiveSensors.removeItemsAt(i, 1);
mActiveVirtualSensors.erase(handle);
+
+ // If this is the last connection, then mark the RecentEventLogger as stale. This is
+ // critical for on-change events since the previous event is sent to a client if the
+ // sensor is already active. If two clients request the sensor at the same time, one
+ // of the clients would receive a stale event.
+ auto logger = mRecentEvent.find(handle);
+ if (logger != mRecentEvent.end()) {
+ logger->second->setLastEventStale();
+ }
delete rec;
size--;
} else {
@@ -1356,10 +1365,11 @@
auto logger = mRecentEvent.find(handle);
if (logger != mRecentEvent.end()) {
sensors_event_t event;
- // It is unlikely that this buffer is empty as the sensor is already active.
- // One possible corner case may be two applications activating an on-change
- // sensor at the same time.
- if(logger->second->populateLastEvent(&event)) {
+ // Verify that the last sensor event was generated from the current activation
+ // of the sensor. If not, it is possible for an on-change sensor to receive a
+ // sensor event that is stale if two clients re-activate the sensor
+ // simultaneously.
+ if(logger->second->populateLastEventIfCurrent(&event)) {
event.sensor = handle;
if (event.version == sizeof(sensors_event_t)) {
if (isWakeUpSensorEvent(event) && !mWakeLockAcquired) {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 339f83b..16003a2 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -30,6 +30,7 @@
"android.hardware.configstore@1.1",
"android.hardware.configstore@1.2",
"android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.common@1.2",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
@@ -79,6 +80,7 @@
],
export_shared_lib_headers: [
"android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.common@1.2",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index cafe26b..440f1e2 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -70,6 +70,8 @@
mName.string());
destroyAllHwcLayers();
}
+
+ mTimeStats.onDestroy(getSequence());
}
void BufferLayer::useSurfaceDamage() {
@@ -234,27 +236,71 @@
const auto& viewport = display->getViewport();
Region visible = tr.transform(visibleRegion.intersect(viewport));
const auto displayId = display->getId();
+ if (!hasHwcLayer(displayId)) {
+ ALOGE("[%s] failed to setPerFrameData: no HWC layer found (%d)",
+ mName.string(), displayId);
+ return;
+ }
+ auto& hwcInfo = getBE().mHwcLayers[displayId];
+ auto& hwcLayer = hwcInfo.layer;
+ auto error = hwcLayer->setVisibleRegion(visible);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ visible.dump(LOG_TAG);
+ }
getBE().compositionInfo.hwc.visibleRegion = visible;
+
+ error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ surfaceDamageRegion.dump(LOG_TAG);
+ }
getBE().compositionInfo.hwc.surfaceDamage = surfaceDamageRegion;
// Sideband layers
if (getBE().compositionInfo.hwc.sidebandStream.get()) {
setCompositionType(displayId, HWC2::Composition::Sideband);
+ ALOGV("[%s] Requesting Sideband composition", mName.string());
+ error = hwcLayer->setSidebandStream(getBE().compositionInfo.hwc.sidebandStream->handle());
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(),
+ getBE().compositionInfo.hwc.sidebandStream->handle(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
getBE().compositionInfo.compositionType = HWC2::Composition::Sideband;
return;
}
- if (getBE().compositionInfo.hwc.skipGeometry) {
- // Device or Cursor layers
- if (mPotentialCursor) {
- ALOGV("[%s] Requesting Cursor composition", mName.string());
- setCompositionType(displayId, HWC2::Composition::Cursor);
- } else {
- ALOGV("[%s] Requesting Device composition", mName.string());
- setCompositionType(displayId, HWC2::Composition::Device);
- }
+ // Device or Cursor layers
+ if (mPotentialCursor) {
+ ALOGV("[%s] Requesting Cursor composition", mName.string());
+ setCompositionType(displayId, HWC2::Composition::Cursor);
+ } else {
+ ALOGV("[%s] Requesting Device composition", mName.string());
+ setCompositionType(displayId, HWC2::Composition::Device);
}
+ ALOGV("setPerFrameData: dataspace = %d", mCurrentDataSpace);
+ error = hwcLayer->setDataspace(mCurrentDataSpace);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ const HdrMetadata& metadata = getDrawingHdrMetadata();
+ error = hwcLayer->setPerFrameMetadata(display->getSupportedPerFrameMetadata(), metadata);
+ if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) {
+ ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
+
+ error = hwcLayer->setColorTransform(getColorTransform());
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace;
getBE().compositionInfo.hwc.hdrMetadata = getDrawingHdrMetadata();
getBE().compositionInfo.hwc.supportedPerFrameMetadata = display->getSupportedPerFrameMetadata();
@@ -291,8 +337,8 @@
nsecs_t desiredPresentTime = getDesiredPresentTime();
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
- const std::string layerName(getName().c_str());
- mTimeStats.setDesiredTime(layerName, mCurrentFrameNumber, desiredPresentTime);
+ const int32_t layerID = getSequence();
+ mTimeStats.setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
std::shared_ptr<FenceTime> frameReadyFence = getCurrentFenceTime();
if (frameReadyFence->isValid()) {
@@ -304,14 +350,14 @@
}
if (presentFence->isValid()) {
- mTimeStats.setPresentFence(layerName, mCurrentFrameNumber, presentFence);
+ mTimeStats.setPresentFence(layerID, mCurrentFrameNumber, presentFence);
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
} else if (mFlinger->getHwComposer().isConnected(HWC_DISPLAY_PRIMARY)) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
const nsecs_t actualPresentTime =
mFlinger->getHwComposer().getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
- mTimeStats.setPresentTime(layerName, mCurrentFrameNumber, actualPresentTime);
+ mTimeStats.setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
mFrameTracker.setActualPresentTime(actualPresentTime);
}
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e75fbdf..e592a8b 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -227,6 +227,7 @@
// BufferItem's that weren't actually queued. This can happen in shared
// buffer mode.
bool queuedBuffer = false;
+ const int32_t layerID = getSequence();
LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode,
getTransformToDisplayInverse(), mFreezeGeometryUpdates);
@@ -247,7 +248,7 @@
// and return early
if (queuedBuffer) {
Mutex::Autolock lock(mQueueItemLock);
- mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber);
+ mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
mQueuedFrames--;
}
@@ -261,7 +262,7 @@
Mutex::Autolock lock(mQueueItemLock);
mQueueItems.clear();
mQueuedFrames = 0;
- mTimeStats.clearLayerRecord(getName().c_str());
+ mTimeStats.clearLayerRecord(layerID);
}
// Once we have hit this state, the shadow queue may no longer
@@ -282,14 +283,13 @@
// Remove any stale buffers that have been dropped during
// updateTexImage
while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
- mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber);
+ mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
mQueuedFrames--;
}
- const std::string layerName(getName().c_str());
- mTimeStats.setAcquireFence(layerName, currentFrameNumber, mQueueItems[0].mFenceTime);
- mTimeStats.setLatchTime(layerName, currentFrameNumber, latchTime);
+ mTimeStats.setAcquireFence(layerID, currentFrameNumber, mQueueItems[0].mFenceTime);
+ mTimeStats.setLatchTime(layerID, currentFrameNumber, latchTime);
mQueueItems.removeAt(0);
}
@@ -327,8 +327,22 @@
return NO_ERROR;
}
-void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>&) {
+void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
+ const auto displayId = display->getId();
+ auto& hwcInfo = getBE().mHwcLayers[displayId];
+ auto& hwcLayer = hwcInfo.layer;
+
+ uint32_t hwcSlot = 0;
+ sp<GraphicBuffer> hwcBuffer;
+ hwcInfo.bufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer, &hwcSlot, &hwcBuffer);
+
auto acquireFence = mConsumer->getCurrentFence();
+ auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
+ getBE().compositionInfo.mBuffer->handle, to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
getBE().compositionInfo.mBufferSlot = mActiveBufferSlot;
getBE().compositionInfo.mBuffer = mActiveBuffer;
getBE().compositionInfo.hwc.fence = acquireFence;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index e52b35a..5df8ade 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -377,6 +377,8 @@
return NO_ERROR;
}
+ const int32_t layerID = getSequence();
+
// Reject if the layer is invalid
uint32_t bufferWidth = s.buffer->width;
uint32_t bufferHeight = s.buffer->height;
@@ -397,7 +399,7 @@
ALOGE("[%s] rejecting buffer: "
"bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h);
- mTimeStats.removeTimeRecord(getName().c_str(), getFrameNumber());
+ mTimeStats.removeTimeRecord(layerID, getFrameNumber());
return BAD_VALUE;
}
@@ -405,7 +407,7 @@
if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) {
// TODO(alecmouri): Fail somewhere upstream if the fence is invalid.
if (!releaseFence->isValid()) {
- mTimeStats.clearLayerRecord(getName().c_str());
+ mTimeStats.clearLayerRecord(layerID);
return UNKNOWN_ERROR;
}
@@ -415,7 +417,7 @@
auto currentStatus = s.acquireFence->getStatus();
if (currentStatus == Fence::Status::Invalid) {
ALOGE("Existing fence has invalid state");
- mTimeStats.clearLayerRecord(getName().c_str());
+ mTimeStats.clearLayerRecord(layerID);
return BAD_VALUE;
}
@@ -423,7 +425,7 @@
if (incomingStatus == Fence::Status::Invalid) {
ALOGE("New fence has invalid state");
mDrawingState.acquireFence = releaseFence;
- mTimeStats.clearLayerRecord(getName().c_str());
+ mTimeStats.clearLayerRecord(layerID);
return BAD_VALUE;
}
@@ -439,7 +441,7 @@
// synchronization is broken, the best we can do is hope fences
// signal in order so the new fence will act like a union
mDrawingState.acquireFence = releaseFence;
- mTimeStats.clearLayerRecord(getName().c_str());
+ mTimeStats.clearLayerRecord(layerID);
return BAD_VALUE;
}
mDrawingState.acquireFence = mergedFence;
@@ -462,16 +464,16 @@
// a GL-composited layer) not at all.
status_t err = bindTextureImage();
if (err != NO_ERROR) {
- mTimeStats.clearLayerRecord(getName().c_str());
+ mTimeStats.clearLayerRecord(layerID);
return BAD_VALUE;
}
}
// TODO(marissaw): properly support mTimeStats
- const std::string layerName(getName().c_str());
- mTimeStats.setPostTime(getName().c_str(), getFrameNumber(), latchTime);
- mTimeStats.setAcquireFence(layerName, getFrameNumber(), getCurrentFenceTime());
- mTimeStats.setLatchTime(layerName, getFrameNumber(), latchTime);
+ mTimeStats.setLayerName(layerID, getName().c_str());
+ mTimeStats.setPostTime(layerID, getFrameNumber(), latchTime);
+ mTimeStats.setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
+ mTimeStats.setLatchTime(layerID, getFrameNumber(), latchTime);
return NO_ERROR;
}
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 3a554c9..263f872 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -62,20 +62,64 @@
const auto& viewport = display->getViewport();
Region visible = tr.transform(visibleRegion.intersect(viewport));
const auto displayId = display->getId();
+ if (!hasHwcLayer(displayId)) {
+ ALOGE("[%s] failed to setPerFrameData: no HWC layer found (%d)",
+ mName.string(), displayId);
+ return;
+ }
+ auto& hwcInfo = getBE().mHwcLayers[displayId];
+ auto& hwcLayer = hwcInfo.layer;
+ auto error = hwcLayer->setVisibleRegion(visible);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ visible.dump(LOG_TAG);
+ }
getBE().compositionInfo.hwc.visibleRegion = visible;
- getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace;
setCompositionType(displayId, HWC2::Composition::SolidColor);
+
+ error = hwcLayer->setDataspace(mCurrentDataSpace);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace;
half4 color = getColor();
- getBE().compositionInfo.hwc.color = {static_cast<uint8_t>(std::round(255.0f * color.r)),
- static_cast<uint8_t>(std::round(255.0f * color.g)),
- static_cast<uint8_t>(std::round(255.0f * color.b)), 255};
+ error = hwcLayer->setColor({static_cast<uint8_t>(std::round(255.0f * color.r)),
+ static_cast<uint8_t>(std::round(255.0f * color.g)),
+ static_cast<uint8_t>(std::round(255.0f * color.b)), 255});
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set color: %s (%d)", mName.string(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
+ getBE().compositionInfo.hwc.color = { static_cast<uint8_t>(std::round(255.0f * color.r)),
+ static_cast<uint8_t>(std::round(255.0f * color.g)),
+ static_cast<uint8_t>(std::round(255.0f * color.b)), 255 };
// Clear out the transform, because it doesn't make sense absent a source buffer
+ error = hwcLayer->setTransform(HWC2::Transform::None);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to clear transform: %s (%d)", mName.string(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ }
getBE().compositionInfo.hwc.transform = HWC2::Transform::None;
+
+ error = hwcLayer->setColorTransform(getColorTransform());
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ }
getBE().compositionInfo.hwc.colorTransform = getColorTransform();
+
+ error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ surfaceDamageRegion.dump(LOG_TAG);
+ }
+ getBE().compositionInfo.hwc.surfaceDamage = surfaceDamageRegion;
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 341dfd5..6f645df 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -37,8 +37,8 @@
#include <ui/DebugUtils.h>
#include <ui/DisplayInfo.h>
#include <ui/PixelFormat.h>
-#include <utils/RefBase.h>
#include <utils/Log.h>
+#include <utils/RefBase.h>
#include "DisplayHardware/DisplaySurface.h"
#include "DisplayHardware/HWComposer.h"
@@ -66,7 +66,8 @@
namespace {
// ordered list of known SDR color modes
-const std::array<ColorMode, 2> sSdrColorModes = {
+const std::array<ColorMode, 3> sSdrColorModes = {
+ ColorMode::DISPLAY_BT2020,
ColorMode::DISPLAY_P3,
ColorMode::SRGB,
};
@@ -96,6 +97,8 @@
return Dataspace::SRGB;
case ColorMode::DISPLAY_P3:
return Dataspace::DISPLAY_P3;
+ case ColorMode::DISPLAY_BT2020:
+ return Dataspace::DISPLAY_BT2020;
case ColorMode::BT2100_HLG:
return Dataspace::BT2020_HLG;
case ColorMode::BT2100_PQ:
@@ -767,9 +770,12 @@
*outMode = iter->second.colorMode;
*outIntent = iter->second.renderIntent;
} else {
- ALOGE("map unknown (%s)/(%s) to default color mode",
- dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
- decodeRenderIntent(intent).c_str());
+ // this is unexpected on a WCG display
+ if (hasWideColorGamut()) {
+ ALOGE("map unknown (%s)/(%s) to default color mode",
+ dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+ decodeRenderIntent(intent).c_str());
+ }
*outDataspace = Dataspace::UNKNOWN;
*outMode = ColorMode::NATIVE;
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 38482c9..918f7de 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -349,10 +349,11 @@
DisplayRenderArea(const sp<const DisplayDevice> device,
ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
: DisplayRenderArea(device, device->getBounds(), device->getWidth(), device->getHeight(),
- rotation) {}
+ device->getCompositionDataSpace(), rotation) {}
DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqWidth,
- uint32_t reqHeight, ui::Transform::orientation_flags rotation)
- : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE,
+ uint32_t reqHeight, ui::Dataspace reqDataSpace,
+ ui::Transform::orientation_flags rotation)
+ : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
getDisplayRotation(rotation, device->getInstallOrientation())),
mDevice(device),
mSourceCrop(sourceCrop) {}
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 163b26c..f0bccaa 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -354,16 +354,26 @@
{
Error error = kDefaultError;
- if (mClient_2_2) {
- mClient_2_2->getColorModes_2_2(display,
- [&](const auto& tmpError, const auto& tmpModes) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
+ if (mClient_2_3) {
+ mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
- *outModes = tmpModes;
- });
+ *outModes = tmpModes;
+ });
+ } else if (mClient_2_2) {
+ mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ for (types::V1_1::ColorMode colorMode : tmpModes) {
+ outModes->push_back(static_cast<ColorMode>(colorMode));
+ }
+ });
} else {
mClient->getColorModes(display,
[&](const auto& tmpError, const auto& tmpModes) {
@@ -555,8 +565,11 @@
RenderIntent renderIntent)
{
hardware::Return<Error> ret(kDefaultError);
- if (mClient_2_2) {
- ret = mClient_2_2->setColorMode_2_2(display, mode, renderIntent);
+ if (mClient_2_3) {
+ ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
+ } else if (mClient_2_2) {
+ ret = mClient_2_2->setColorMode_2_2(display, static_cast<types::V1_1::ColorMode>(mode),
+ renderIntent);
} else {
ret = mClient->setColorMode(display,
static_cast<types::V1_0::ColorMode>(mode));
@@ -934,15 +947,22 @@
}
Error error = kDefaultError;
- mClient_2_2->getRenderIntents(display, colorMode,
- [&](const auto& tmpError, const auto& tmpKeys) {
+
+ auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
error = tmpError;
if (error != Error::NONE) {
return;
}
*outRenderIntents = tmpKeys;
- });
+ };
+
+ if (mClient_2_3) {
+ mClient_2_3->getRenderIntents_2_3(display, colorMode, getRenderIntentsLambda);
+ } else {
+ mClient_2_2->getRenderIntents(display, static_cast<types::V1_1::ColorMode>(colorMode),
+ getRenderIntentsLambda);
+ }
return error;
}
@@ -955,14 +975,14 @@
}
Error error = kDefaultError;
- mClient_2_2->getDataspaceSaturationMatrix(dataspace, [&](const auto& tmpError, const auto& tmpMatrix) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outMatrix = mat4(tmpMatrix.data());
- });
+ mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
+ [&](const auto& tmpError, const auto& tmpMatrix) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outMatrix = mat4(tmpMatrix.data());
+ });
return error;
}
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 94be6e9..4188352 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -49,10 +49,10 @@
using types::V1_0::Hdr;
using types::V1_0::Transform;
-using types::V1_1::ColorMode;
-using types::V1_1::Dataspace;
using types::V1_1::PixelFormat;
using types::V1_1::RenderIntent;
+using types::V1_2::ColorMode;
+using types::V1_2::Dataspace;
using V2_1::Config;
using V2_1::Display;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 3a40648..393041d 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -184,7 +184,6 @@
auto newDisplay = std::make_unique<Display>(
*mComposer.get(), mPowerAdvisor, mCapabilities, displayId, displayType);
- newDisplay->setFrequencyScaleParameters(mFrequencyScaler);
newDisplay->setConnected(true);
mDisplays.emplace(displayId, std::move(newDisplay));
} else if (connection == Connection::Disconnected) {
@@ -224,14 +223,6 @@
return static_cast<Error>(mComposer->executeCommands());
}
-void Device::setDisplayFrequencyScaleParameters(Device::FrequencyScaler frequencyScaler) {
- mFrequencyScaler = frequencyScaler;
-}
-
-Device::FrequencyScaler Device::getDisplayFrequencyScaleParameters() {
- return mFrequencyScaler;
-}
-
// Display methods
Display::Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor,
@@ -271,7 +262,6 @@
mWidth(-1),
mHeight(-1),
mVsyncPeriod(-1),
- mFrequencyScaler(display.mFrequencyScaler),
mDpiX(-1),
mDpiY(-1) {}
@@ -711,10 +701,6 @@
mIsConnected = connected;
}
-void Display::setFrequencyScaleParameters(Device::FrequencyScaler frequencyScaler) {
- mFrequencyScaler = frequencyScaler;
-}
-
int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute)
{
int32_t value = 0;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 363adb5..d274631 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -23,7 +23,6 @@
#undef HWC2_INCLUDE_STRINGIFICATION
#undef HWC2_USE_CPP11
-#include <cutils/properties.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/GraphicTypes.h>
@@ -85,11 +84,6 @@
public:
explicit Device(std::unique_ptr<android::Hwc2::Composer> composer);
- struct FrequencyScaler {
- int32_t multiplier = 1;
- int32_t divisor = 1;
- };
-
void registerCallback(ComposerCallback* callback, int32_t sequenceId);
// Required by HWC2
@@ -121,9 +115,6 @@
// This method provides an explicit way to flush state changes to HWC.
Error flushCommands();
- void setDisplayFrequencyScaleParameters(FrequencyScaler frequecyScaler);
- FrequencyScaler getDisplayFrequencyScaleParameters();
-
private:
// Initialization methods
@@ -134,7 +125,6 @@
std::unordered_set<Capability> mCapabilities;
std::unordered_map<hwc2_display_t, std::unique_ptr<Display>> mDisplays;
android::Hwc2::impl::PowerAdvisor mPowerAdvisor;
- FrequencyScaler mFrequencyScaler;
bool mRegisteredCallback = false;
};
@@ -199,8 +189,7 @@
int32_t getWidth() const { return mWidth; }
int32_t getHeight() const { return mHeight; }
- nsecs_t getVsyncPeriod() const {
- return mVsyncPeriod * mFrequencyScaler.multiplier / mFrequencyScaler.divisor; }
+ nsecs_t getVsyncPeriod() const { return mVsyncPeriod; }
float getDpiX() const { return mDpiX; }
float getDpiY() const { return mDpiY; }
@@ -213,7 +202,6 @@
int32_t mWidth;
int32_t mHeight;
nsecs_t mVsyncPeriod;
- Device::FrequencyScaler mFrequencyScaler;
float mDpiX;
float mDpiY;
};
@@ -281,7 +269,6 @@
hwc2_display_t getId() const { return mId; }
bool isConnected() const { return mIsConnected; }
void setConnected(bool connected); // For use by Device only
- void setFrequencyScaleParameters(Device::FrequencyScaler frequencyScaler);
private:
int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
@@ -306,7 +293,6 @@
hwc2_display_t mId;
bool mIsConnected;
DisplayType mType;
- Device::FrequencyScaler mFrequencyScaler;
std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
};
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 6b56ca4..d827fd2 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -814,15 +814,4 @@
return mDisplayData[displayId].hwcDisplay->getId();
}
-void HWComposer::setDisplayFrequencyScaleParameters(
- HWC2::Device::FrequencyScaler frequencyScaler)
-{
- mHwcDevice->setDisplayFrequencyScaleParameters(frequencyScaler);
-}
-
-HWC2::Device::FrequencyScaler HWComposer::getDisplayFrequencyScaleParameters()
-{
- return mHwcDevice->getDisplayFrequencyScaleParameters();
-}
-
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index cca1f3b..4777ca9 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -186,14 +186,6 @@
android::Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); }
std::optional<hwc2_display_t> getHwcDisplayId(int32_t displayId) const;
-
- // ------------------------------------------------------------------------
- // These functions set and get the frequencyScaler. The frequencyScaler holds
- // a multiplier and divisor for virtually scaling the panel frequency in
- // software. This is used to simulate different panel frequencies when
- // panel hardware is not available.
- void setDisplayFrequencyScaleParameters(HWC2::Device::FrequencyScaler frequencyScaler);
- HWC2::Device::FrequencyScaler getDisplayFrequencyScaleParameters();
private:
// For unit tests
friend TestableSurfaceFlinger;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8afd3b3..88c3c8a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -102,6 +102,7 @@
mCurrentState.hdrMetadata.validTypes = 0;
mCurrentState.surfaceDamageRegion.clear();
mCurrentState.api = -1;
+ mCurrentState.hasColorTransform = false;
// drawing state & current state are identical
mDrawingState = mCurrentState;
@@ -457,18 +458,6 @@
}
auto& hwcInfo = getBE().mHwcLayers[displayId];
- // Device or Cursor layers
- if (mPotentialCursor) {
- ALOGV("[%s] Requesting Cursor composition", mName.string());
- setCompositionType(displayId, HWC2::Composition::Cursor);
- } else {
- ALOGV("[%s] Requesting Device composition", mName.string());
- setCompositionType(displayId, HWC2::Composition::Device);
- }
-
- // Need to program geometry parts
- getBE().compositionInfo.hwc.skipGeometry = false;
-
// enable this layer
hwcInfo.forceClientComposition = false;
@@ -476,6 +465,8 @@
hwcInfo.forceClientComposition = true;
}
+ auto& hwcLayer = hwcInfo.layer;
+
// this gives us only the "orientation" component of the transform
const State& s(getDrawingState());
auto blendMode = HWC2::BlendMode::None;
@@ -483,6 +474,12 @@
blendMode =
mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage;
}
+ auto error = hwcLayer->setBlendMode(blendMode);
+ ALOGE_IF(error != HWC2::Error::None,
+ "[%s] Failed to set blend mode %s:"
+ " %s (%d)",
+ mName.string(), to_string(blendMode).c_str(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
getBE().compositionInfo.hwc.blendMode = blendMode;
// apply the layer's transform, followed by the display's global transform
@@ -522,14 +519,39 @@
}
const ui::Transform& tr = display->getTransform();
Rect transformedFrame = tr.transform(frame);
+ error = hwcLayer->setDisplayFrame(transformedFrame);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", mName.string(),
+ transformedFrame.left, transformedFrame.top, transformedFrame.right,
+ transformedFrame.bottom, to_string(error).c_str(), static_cast<int32_t>(error));
+ } else {
+ hwcInfo.displayFrame = transformedFrame;
+ }
getBE().compositionInfo.hwc.displayFrame = transformedFrame;
FloatRect sourceCrop = computeCrop(display);
+ error = hwcLayer->setSourceCrop(sourceCrop);
+ if (error != HWC2::Error::None) {
+ ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
+ "%s (%d)",
+ mName.string(), sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom,
+ to_string(error).c_str(), static_cast<int32_t>(error));
+ } else {
+ hwcInfo.sourceCrop = sourceCrop;
+ }
getBE().compositionInfo.hwc.sourceCrop = sourceCrop;
float alpha = static_cast<float>(getAlpha());
+ error = hwcLayer->setPlaneAlpha(alpha);
+ ALOGE_IF(error != HWC2::Error::None,
+ "[%s] Failed to set plane alpha %.3f: "
+ "%s (%d)",
+ mName.string(), alpha, to_string(error).c_str(), static_cast<int32_t>(error));
getBE().compositionInfo.hwc.alpha = alpha;
+ error = hwcLayer->setZOrder(z);
+ ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)", mName.string(), z,
+ to_string(error).c_str(), static_cast<int32_t>(error));
getBE().compositionInfo.hwc.z = z;
int type = s.type;
@@ -543,6 +565,10 @@
}
}
+ error = hwcLayer->setInfo(type, appId);
+ ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set info (%d)", mName.string(),
+ static_cast<int32_t>(error));
+
getBE().compositionInfo.hwc.type = type;
getBE().compositionInfo.hwc.appId = appId;
@@ -582,9 +608,16 @@
if (orientation & ui::Transform::ROT_INVALID) {
// we can only handle simple transformation
hwcInfo.forceClientComposition = true;
+ getBE().mHwcLayers[displayId].compositionType = HWC2::Composition::Client;
} else {
auto transform = static_cast<HWC2::Transform>(orientation);
hwcInfo.transform = transform;
+ auto error = hwcLayer->setTransform(transform);
+ ALOGE_IF(error != HWC2::Error::None,
+ "[%s] Failed to set transform %s: "
+ "%s (%d)",
+ mName.string(), to_string(transform).c_str(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
getBE().compositionInfo.hwc.transform = transform;
}
}
@@ -665,16 +698,25 @@
clearWithOpenGL(renderArea, 0, 0, 0, 0);
}
-void Layer::setCompositionType(int32_t displayId, HWC2::Composition type, bool /*callIntoHwc*/) {
+void Layer::setCompositionType(int32_t displayId, HWC2::Composition type, bool callIntoHwc) {
if (getBE().mHwcLayers.count(displayId) == 0) {
ALOGE("setCompositionType called without a valid HWC layer");
return;
- } else {
- if (getBE().mHwcLayers[displayId].compositionType != type) {
- ALOGV("setCompositionType: Changing compositionType from %s to %s",
- to_string(getBE().mHwcLayers[displayId].compositionType).c_str(),
- to_string(type).c_str());
- getBE().mHwcLayers[displayId].compositionType = type;
+ }
+ auto& hwcInfo = getBE().mHwcLayers[displayId];
+ auto& hwcLayer = hwcInfo.layer;
+ ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", (hwcLayer)->getId(), to_string(type).c_str(),
+ static_cast<int>(callIntoHwc));
+ if (hwcInfo.compositionType != type) {
+ ALOGV(" actually setting");
+ hwcInfo.compositionType = type;
+ if (callIntoHwc) {
+ auto error = (hwcLayer)->setCompositionType(type);
+ ALOGE_IF(error != HWC2::Error::None,
+ "[%s] Failed to set "
+ "composition type %s: %s (%d)",
+ mName.string(), to_string(type).c_str(), to_string(error).c_str(),
+ static_cast<int32_t>(error));
}
}
}
@@ -1428,14 +1470,15 @@
void Layer::onDisconnect() {
Mutex::Autolock lock(mFrameEventHistoryMutex);
mFrameEventHistory.onDisconnect();
- mTimeStats.onDisconnect(getName().c_str());
+ mTimeStats.onDisconnect(getSequence());
}
void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
if (newTimestamps) {
- mTimeStats.setPostTime(getName().c_str(), newTimestamps->frameNumber,
- newTimestamps->postedTime);
+ const int32_t layerID = getSequence();
+ mTimeStats.setLayerName(layerID, getName().c_str());
+ mTimeStats.setPostTime(layerID, newTimestamps->frameNumber, newTimestamps->postedTime);
}
Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -1547,11 +1590,15 @@
}
bool Layer::setColorTransform(const mat4& matrix) {
+ static const mat4 identityMatrix = mat4();
+
if (mCurrentState.colorTransform == matrix) {
return false;
}
++mCurrentState.sequence;
mCurrentState.colorTransform = matrix;
+ mCurrentState.hasColorTransform = matrix != identityMatrix;
+ mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -1561,8 +1608,7 @@
}
bool Layer::hasColorTransform() const {
- static const mat4 identityMatrix = mat4();
- return getDrawingState().colorTransform != identityMatrix;
+ return getDrawingState().hasColorTransform;
}
bool Layer::isLegacyDataSpace() const {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index e2d1178..5d05f05 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -182,6 +182,7 @@
sp<NativeHandle> sidebandStream;
mat4 colorTransform;
+ bool hasColorTransform;
};
explicit Layer(const LayerCreationArgs& args);
diff --git a/services/surfaceflinger/LayerBE.cpp b/services/surfaceflinger/LayerBE.cpp
index c9b7933..70b00dd 100644
--- a/services/surfaceflinger/LayerBE.cpp
+++ b/services/surfaceflinger/LayerBE.cpp
@@ -65,9 +65,7 @@
}
void LayerBE::onLayerDisplayed(const sp<Fence>& releaseFence) {
- if (mLayer) {
- mLayer->onLayerDisplayed(releaseFence);
- }
+ mLayer->onLayerDisplayed(releaseFence);
}
void LayerBE::clear(renderengine::RenderEngine& engine) {
diff --git a/services/surfaceflinger/LayerBE.h b/services/surfaceflinger/LayerBE.h
index d63d16f..463c46c 100644
--- a/services/surfaceflinger/LayerBE.h
+++ b/services/surfaceflinger/LayerBE.h
@@ -34,14 +34,13 @@
struct CompositionInfo {
std::string layerName;
- HWC2::Composition compositionType = HWC2::Composition::Invalid;
+ HWC2::Composition compositionType;
bool firstClear = false;
sp<GraphicBuffer> mBuffer = nullptr;
int mBufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
std::shared_ptr<LayerBE> layer;
struct {
std::shared_ptr<HWC2::Layer> hwcLayer;
- bool skipGeometry = true;
int32_t displayId = -1;
sp<Fence> fence;
HWC2::BlendMode blendMode = HWC2::BlendMode::Invalid;
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 3c11e73..9bad6de 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -1,5 +1,6 @@
#pragma once
+#include <ui/GraphicTypes.h>
#include <ui/Transform.h>
#include <functional>
@@ -19,9 +20,11 @@
static float getCaptureFillValue(CaptureFill captureFill);
RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
+ ui::Dataspace reqDataSpace,
ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
: mReqWidth(reqWidth),
mReqHeight(reqHeight),
+ mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
mRotationFlags(rotation) {}
@@ -66,6 +69,9 @@
int getReqWidth() const { return mReqWidth; };
int getReqHeight() const { return mReqHeight; };
+ // Returns the composition data space of the render area.
+ ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
+
// Returns the fill color of the physical render area. Regions not
// covered by any rendered layer should be filled with this color.
CaptureFill getCaptureFill() const { return mCaptureFill; };
@@ -73,6 +79,7 @@
private:
const uint32_t mReqWidth;
const uint32_t mReqHeight;
+ const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
const ui::Transform::orientation_flags mRotationFlags;
};
diff --git a/services/surfaceflinger/RenderEngine/TEST_MAPPING b/services/surfaceflinger/RenderEngine/TEST_MAPPING
new file mode 100644
index 0000000..995dba1
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "librenderengine_test"
+ }
+ ]
+}
diff --git a/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.cpp
index 5c4c3d5..dbf50c5 100644
--- a/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.cpp
@@ -650,6 +650,13 @@
} while (true);
}
+status_t GLES20RenderEngine::drawLayers(const DisplaySettings& /*settings*/,
+ const std::vector<LayerSettings>& /*layers*/,
+ ANativeWindowBuffer* const /*buffer*/,
+ base::unique_fd* /*displayFence*/) const {
+ return NO_ERROR;
+}
+
void GLES20RenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
ui::Transform::orientation_flags rotation) {
int32_t l = sourceCrop.left;
diff --git a/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.h
index 4f03a90..b08e096 100644
--- a/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.h
@@ -73,6 +73,10 @@
void unbindFrameBuffer(Framebuffer* framebuffer) override;
void checkErrors() const override;
+ status_t drawLayers(const DisplaySettings& settings, const std::vector<LayerSettings>& layers,
+ ANativeWindowBuffer* const buffer,
+ base::unique_fd* displayFence) const override;
+
// internal to RenderEngine
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
EGLConfig getEGLConfig() const { return mEGLConfig; }
diff --git a/services/surfaceflinger/RenderEngine/include/renderengine/DisplaySettings.h b/services/surfaceflinger/RenderEngine/include/renderengine/DisplaySettings.h
new file mode 100644
index 0000000..5941cdf
--- /dev/null
+++ b/services/surfaceflinger/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;
+
+ // 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;
+
+ // Global transform to apply to all layers.
+ mat4 globalTransform;
+
+ // Maximum luminance pulled from the display's HDR capabilities.
+ float maxLuminence;
+
+ // Output dataspace that will be populated if wide color gamut is used, or
+ // DataSpace::UNKNOWN otherwise.
+ ui::Dataspace outputDataspace;
+
+ // Additional color transform to apply in linear space after transforming
+ // to the output dataspace.
+ mat4 colorTransform;
+
+ // 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;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/include/renderengine/LayerSettings.h b/services/surfaceflinger/RenderEngine/include/renderengine/LayerSettings.h
new file mode 100644
index 0000000..facea21
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/include/renderengine/LayerSettings.h
@@ -0,0 +1,92 @@
+/*
+ * 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;
+
+ // Texture identifier to bind the external texture to.
+ // TODO(alecmouri): This is GL-specific...make the type backend-agnostic.
+ uint32_t textureName;
+
+ // Whether to use filtering when rendering the texture.
+ bool useTextureFiltering;
+
+ // Transform matrix to apply to texture coordinates.
+ mat4 textureTransform;
+
+ // Wheteher to use pre-multiplied alpha
+ bool usePremultipliedAlpha;
+
+ // HDR color-space setting for Y410.
+ bool isY410BT2020;
+};
+
+// Metadata describing the layer geometry.
+struct Geometry {
+ // Boundaries of the layer.
+ FloatRect boundaries;
+
+ // Transform matrix to apply to mesh coordinates.
+ mat4 positionTransform;
+};
+
+// Descriptor of the source pixels for this layer.
+struct PixelSource {
+ // Source 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;
+};
+
+// The settings that RenderEngine requires for correctly rendering a Layer.
+struct LayerSettings {
+ // Geometry information
+ Geometry geometry;
+
+ // Source pixels for this layer.
+ PixelSource source;
+
+ // Alpha option to apply to the source pixels
+ half alpha;
+
+ // Color space describing how the source pixels should be interpreted.
+ ui::Dataspace sourceDataspace;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/include/renderengine/RenderEngine.h b/services/surfaceflinger/RenderEngine/include/renderengine/RenderEngine.h
index 122271f..f5d3d6b 100644
--- a/services/surfaceflinger/RenderEngine/include/renderengine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/include/renderengine/RenderEngine.h
@@ -23,8 +23,10 @@
#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>
@@ -64,6 +66,11 @@
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<Surface> createSurface() = 0;
virtual std::unique_ptr<Image> createImage() = 0;
@@ -133,6 +140,37 @@
// queries
virtual size_t getMaxTextureSize() const = 0;
virtual size_t getMaxViewportDims() const = 0;
+
+ // ----- END DEPRECATED INTERFACE -----
+
+ // ----- BEGIN NEW INTERFACE -----
+
+ // 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 settings 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 displayFence 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& settings,
+ const std::vector<LayerSettings>& layers,
+ ANativeWindowBuffer* const buffer,
+ base::unique_fd* displayFence) const = 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 {
diff --git a/services/surfaceflinger/RenderEngine/tests/Android.bp b/services/surfaceflinger/RenderEngine/tests/Android.bp
new file mode 100644
index 0000000..65b7c82
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/tests/Android.bp
@@ -0,0 +1,36 @@
+// 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: [
+ "libcutils",
+ "libEGL",
+ "libGLESv2",
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "libui",
+ "libutils",
+ ],
+}
diff --git a/services/surfaceflinger/RenderEngine/tests/RenderEngineTest.cpp b/services/surfaceflinger/RenderEngine/tests/RenderEngineTest.cpp
new file mode 100644
index 0000000..345c7ea
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/tests/RenderEngineTest.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 <ui/PixelFormat.h>
+
+namespace android {
+
+class RenderEngineTest : public ::testing::Test {
+public:
+ RenderEngineTest() {
+ // Initialize with some sane defaults.
+ // TODO(alecmouri): This should probably be the same instance used by
+ // SurfaceFlinger eventually.
+ mRE = renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888),
+ 0);
+ }
+
+ status_t drawEmptyLayers() {
+ renderengine::DisplaySettings settings;
+ std::vector<renderengine::LayerSettings> layers;
+ // Meaningless buffer since we don't do any drawing
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ base::unique_fd fence;
+ return mRE->drawLayers(settings, layers, buffer->getNativeBuffer(), &fence);
+ }
+
+private:
+ std::unique_ptr<renderengine::RenderEngine> mRE;
+};
+
+TEST_F(RenderEngineTest, drawLayers_noLayersToDraw_works) {
+ status_t result = drawEmptyLayers();
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index cba9181..172c418 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -25,6 +25,7 @@
#include <algorithm>
#include <log/log.h>
+#include <cutils/properties.h>
#include <utils/String8.h>
#include <utils/Thread.h>
#include <utils/Trace.h>
@@ -520,27 +521,12 @@
void DispSync::setPeriod(nsecs_t period) {
Mutex::Autolock lock(mMutex);
- mPeriodBase = mPeriod = period;
+ mPeriod = period;
mPhase = 0;
mReferenceTime = 0;
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
}
-void DispSync::scalePeriod(HWC2::Device::FrequencyScaler frequencyScaler) {
- Mutex::Autolock lock(mMutex);
-
- // if only 1 of the properties is updated, we will get to this
- // point "attempting" to set the scale to 1 when it is already
- // 1. Check that special case so that we don't do a useless
- // update of the model.
- if ((frequencyScaler.multiplier == 1) &&
- (frequencyScaler.divisor == 1) &&
- (mPeriod == mPeriodBase)) return;
-
- mPeriod = mPeriodBase * frequencyScaler.multiplier / frequencyScaler.divisor;
- mThread->updateModel(mPeriod, mPhase, mReferenceTime);
-}
-
nsecs_t DispSync::getPeriod() {
// lock mutex as mPeriod changes multiple times in updateModelLocked
Mutex::Autolock lock(mMutex);
@@ -565,7 +551,7 @@
// Exclude the min and max from the average
durationSum -= minDuration + maxDuration;
- mPeriodBase = mPeriod = durationSum / (mNumResyncSamples - 3);
+ mPeriod = durationSum / (mNumResyncSamples - 3);
ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod));
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index 5b511f4..5d19093 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -24,7 +24,6 @@
#include <utils/Timers.h>
#include <ui/FenceTime.h>
-#include <DisplayHardware/HWC2.h>
#include <memory>
@@ -49,7 +48,6 @@
virtual bool addResyncSample(nsecs_t timestamp) = 0;
virtual void endResync() = 0;
virtual void setPeriod(nsecs_t period) = 0;
- virtual void scalePeriod(HWC2::Device::FrequencyScaler) = 0;
virtual nsecs_t getPeriod() = 0;
virtual void setRefreshSkipCount(int count) = 0;
virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) = 0;
@@ -120,12 +118,6 @@
// turned on. It should NOT be used after that.
void setPeriod(nsecs_t period) override;
- // The scalePeriod method applies the multiplier and divisor to
- // scale the vsync event model's period. The function is added
- // for an experimental test mode and should not be used outside
- // of that purpose.
- void scalePeriod(HWC2::Device::FrequencyScaler frequencyScaler);
-
// The getPeriod method returns the current vsync period.
nsecs_t getPeriod() override;
@@ -188,7 +180,6 @@
// mPeriod is the computed period of the modeled vsync events in
// nanoseconds.
nsecs_t mPeriod;
- nsecs_t mPeriodBase;
// mPhase is the phase offset of the modeled vsync events. It is the
// number of nanoseconds from time 0 to the first vsync event.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f0723e8..c5a4cac 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -124,6 +124,7 @@
case ColorMode::ADOBE_RGB:
case ColorMode::DCI_P3:
case ColorMode::BT2020:
+ case ColorMode::DISPLAY_BT2020:
case ColorMode::BT2100_PQ:
case ColorMode::BT2100_HLG:
return true;
@@ -1522,13 +1523,8 @@
preComposition();
rebuildLayerStacks();
calculateWorkingSet();
-
for (const auto& [token, display] : mDisplays) {
- const auto displayId = display->getId();
beginFrame(display);
- for (auto& compositionInfo : getBE().mCompositionInfo[displayId]) {
- setUpHWComposer(compositionInfo);
- }
prepareFrame(display);
doDebugFlashRegions(display, repaintEverything);
doComposition(display, repaintEverything);
@@ -1909,6 +1905,8 @@
mTimeStats.incrementClientCompositionFrames();
}
+ mTimeStats.setPresentFenceGlobal(presentFenceTime);
+
if (display && getHwComposer().isConnected(display->getId()) &&
display->getPowerMode() == HWC_POWER_MODE_OFF) {
return;
@@ -2008,6 +2006,7 @@
// can only be one of
// - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced)
// - Dataspace::DISPLAY_P3
+// - Dataspace::DISPLAY_BT2020
// The returned HDR data space is one of
// - Dataspace::UNKNOWN
// - Dataspace::BT2020_HLG
@@ -2021,6 +2020,12 @@
switch (layer->getDataSpace()) {
case Dataspace::V0_SCRGB:
case Dataspace::V0_SCRGB_LINEAR:
+ case Dataspace::BT2020:
+ case Dataspace::BT2020_ITU:
+ case Dataspace::BT2020_LINEAR:
+ case Dataspace::DISPLAY_BT2020:
+ bestDataSpace = Dataspace::DISPLAY_BT2020;
+ break;
case Dataspace::DISPLAY_P3:
bestDataSpace = Dataspace::DISPLAY_P3;
break;
@@ -2081,127 +2086,6 @@
display->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
}
-void SurfaceFlinger::configureSidebandComposition(const CompositionInfo& compositionInfo) const
-{
- HWC2::Error error;
- LOG_ALWAYS_FATAL_IF(compositionInfo.hwc.sidebandStream == nullptr,
- "CompositionType is sideband, but sideband stream is nullptr");
- error = (compositionInfo.hwc.hwcLayer)
- ->setSidebandStream(compositionInfo.hwc.sidebandStream->handle());
- if (error != HWC2::Error::None) {
- ALOGE("[SF] Failed to set sideband stream %p: %s (%d)",
- compositionInfo.hwc.sidebandStream->handle(), to_string(error).c_str(),
- static_cast<int32_t>(error));
- }
-}
-
-void SurfaceFlinger::configureHwcCommonData(const CompositionInfo& compositionInfo) const
-{
- HWC2::Error error;
-
- if (!compositionInfo.hwc.skipGeometry) {
- error = (compositionInfo.hwc.hwcLayer)->setBlendMode(compositionInfo.hwc.blendMode);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set blend mode %s:"
- " %s (%d)",
- to_string(compositionInfo.hwc.blendMode).c_str(), to_string(error).c_str(),
- static_cast<int32_t>(error));
-
- error = (compositionInfo.hwc.hwcLayer)->setDisplayFrame(compositionInfo.hwc.displayFrame);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set the display frame [%d, %d, %d, %d] %s (%d)",
- compositionInfo.hwc.displayFrame.left,
- compositionInfo.hwc.displayFrame.right,
- compositionInfo.hwc.displayFrame.top,
- compositionInfo.hwc.displayFrame.bottom,
- to_string(error).c_str(), static_cast<int32_t>(error));
-
- error = (compositionInfo.hwc.hwcLayer)->setSourceCrop(compositionInfo.hwc.sourceCrop);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: %s (%d)",
- compositionInfo.hwc.sourceCrop.left,
- compositionInfo.hwc.sourceCrop.right,
- compositionInfo.hwc.sourceCrop.top,
- compositionInfo.hwc.sourceCrop.bottom,
- to_string(error).c_str(), static_cast<int32_t>(error));
-
- error = (compositionInfo.hwc.hwcLayer)->setPlaneAlpha(compositionInfo.hwc.alpha);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set plane alpha %.3f: "
- "%s (%d)",
- compositionInfo.hwc.alpha,
- to_string(error).c_str(), static_cast<int32_t>(error));
-
-
- error = (compositionInfo.hwc.hwcLayer)->setZOrder(compositionInfo.hwc.z);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set Z %u: %s (%d)",
- compositionInfo.hwc.z,
- to_string(error).c_str(), static_cast<int32_t>(error));
-
- error = (compositionInfo.hwc.hwcLayer)
- ->setInfo(compositionInfo.hwc.type, compositionInfo.hwc.appId);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set info (%d)",
- static_cast<int32_t>(error));
-
- error = (compositionInfo.hwc.hwcLayer)->setTransform(compositionInfo.hwc.transform);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set transform %s: "
- "%s (%d)",
- to_string(compositionInfo.hwc.transform).c_str(), to_string(error).c_str(),
- static_cast<int32_t>(error));
- }
-
- error = (compositionInfo.hwc.hwcLayer)->setCompositionType(compositionInfo.compositionType);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set composition type: %s (%d)",
- to_string(error).c_str(), static_cast<int32_t>(error));
-
- error = (compositionInfo.hwc.hwcLayer)->setDataspace(compositionInfo.hwc.dataspace);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set dataspace: %s (%d)",
- to_string(error).c_str(), static_cast<int32_t>(error));
-
- error = (compositionInfo.hwc.hwcLayer)->setPerFrameMetadata(
- compositionInfo.hwc.supportedPerFrameMetadata, compositionInfo.hwc.hdrMetadata);
- ALOGE_IF(error != HWC2::Error::None && error != HWC2::Error::Unsupported,
- "[SF] Failed to set hdrMetadata: %s (%d)",
- to_string(error).c_str(), static_cast<int32_t>(error));
-
- if (compositionInfo.compositionType == HWC2::Composition::SolidColor) {
- error = (compositionInfo.hwc.hwcLayer)->setColor(compositionInfo.hwc.color);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set color: %s (%d)",
- to_string(error).c_str(), static_cast<int32_t>(error));
- }
-
- error = (compositionInfo.hwc.hwcLayer)->setVisibleRegion(compositionInfo.hwc.visibleRegion);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set visible region: %s (%d)",
- to_string(error).c_str(), static_cast<int32_t>(error));
-
- error = (compositionInfo.hwc.hwcLayer)->setSurfaceDamage(compositionInfo.hwc.surfaceDamage);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set surface damage: %s (%d)",
- to_string(error).c_str(), static_cast<int32_t>(error));
-
- error = (compositionInfo.hwc.hwcLayer)->setColorTransform(compositionInfo.hwc.colorTransform);
-}
-
-void SurfaceFlinger::configureDeviceComposition(const CompositionInfo& compositionInfo) const
-{
- HWC2::Error error;
-
- if (compositionInfo.hwc.fence) {
- error = (compositionInfo.hwc.hwcLayer)->setBuffer(compositionInfo.mBufferSlot,
- compositionInfo.mBuffer, compositionInfo.hwc.fence);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set buffer: %s (%d)",
- to_string(error).c_str(), static_cast<int32_t>(error));
- }
-}
-
void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& display)
{
bool dirty = !display->getDirtyRegion(false).isEmpty();
@@ -2247,42 +2131,6 @@
display->getId(), result, strerror(-result));
}
-void SurfaceFlinger::setUpHWComposer(const CompositionInfo& compositionInfo) {
- ATRACE_CALL();
- ALOGV("setUpHWComposer");
-
- switch (compositionInfo.compositionType)
- {
- case HWC2::Composition::Invalid:
- break;
-
- case HWC2::Composition::Client:
- if (compositionInfo.hwc.hwcLayer) {
- auto error = (compositionInfo.hwc.hwcLayer)->
- setCompositionType(compositionInfo.compositionType);
- ALOGE_IF(error != HWC2::Error::None,
- "[SF] Failed to set composition type: %s (%d)",
- to_string(error).c_str(), static_cast<int32_t>(error));
- }
- break;
-
- case HWC2::Composition::Sideband:
- configureHwcCommonData(compositionInfo);
- configureSidebandComposition(compositionInfo);
- break;
-
- case HWC2::Composition::SolidColor:
- configureHwcCommonData(compositionInfo);
- break;
-
- case HWC2::Composition::Device:
- case HWC2::Composition::Cursor:
- configureHwcCommonData(compositionInfo);
- configureDeviceComposition(compositionInfo);
- break;
- }
-}
-
void SurfaceFlinger::doComposition(const sp<DisplayDevice>& display, bool repaintEverything) {
ATRACE_CALL();
ALOGV("doComposition");
@@ -2325,14 +2173,15 @@
}
display->onSwapBuffersCompleted();
display->makeCurrent();
- for (auto& compositionInfo : getBE().mCompositionInfo[displayId]) {
+ for (auto& layer : display->getVisibleLayersSortedByZ()) {
sp<Fence> releaseFence = Fence::NO_FENCE;
+
// The layer buffer from the previous frame (if any) is released
// by HWC only when the release fence from this frame (if any) is
// signaled. Always get the release fence from HWC first.
- auto hwcLayer = compositionInfo.hwc.hwcLayer;
- if ((displayId >= 0) && hwcLayer) {
- releaseFence = getBE().mHwc->getLayerReleaseFence(displayId, hwcLayer.get());
+ auto hwcLayer = layer->getHwcLayer(displayId);
+ if (displayId >= 0) {
+ releaseFence = getBE().mHwc->getLayerReleaseFence(displayId, hwcLayer);
}
// If the layer was client composited in the previous frame, we
@@ -2340,14 +2189,12 @@
// Since we do not track that, always merge with the current
// client target acquire fence when it is available, even though
// this is suboptimal.
- if (compositionInfo.compositionType == HWC2::Composition::Client) {
+ if (layer->getCompositionType(displayId) == HWC2::Composition::Client) {
releaseFence = Fence::merge("LayerRelease", releaseFence,
display->getClientTargetAcquireFence());
}
- if (compositionInfo.layer) {
- compositionInfo.layer->onLayerDisplayed(releaseFence);
- }
+ layer->getBE().onLayerDisplayed(releaseFence);
}
// We've got a list of layers needing fences, that are disjoint with
@@ -4135,6 +3982,10 @@
getHwComposer().setPowerMode(type, mode);
}
+ if (display->isPrimary()) {
+ mTimeStats.setPowerMode(mode);
+ }
+
ALOGD("Finished setting power mode %d on display %d", mode, displayId);
}
@@ -4927,9 +4778,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1029 are currently use for backdoors. The code
+ // Numbers from 1000 to 1030 are currently use for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1029) {
+ if (code >= 1000 && code <= 1030) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5125,11 +4976,9 @@
repaintEverything();
return NO_ERROR;
}
- // TODO(b/111505327): Find out whether the usage of 1024 can switch to 1030,
- // deprecate 1024 if they can.
- case 1024: { // Does device have wide color gamut display?
- reply->writeBool(hasWideColorDisplay);
- return NO_ERROR;
+ // Deprecate, use 1030 to check whether the device is color managed.
+ case 1024: {
+ return NAME_NOT_FOUND;
}
case 1025: { // Set layer tracing
n = data.readInt32();
@@ -5180,54 +5029,6 @@
reply->writeBool(getBE().mHwc->isUsingVrComposer());
return NO_ERROR;
}
- case 1029: {
- // Code 1029 is an experimental feature that allows applications to
- // simulate a high frequency panel by setting a multiplier and divisor
- // on the VSYNC-sf clock. If either the multiplier or divisor are
- // 0, then the code simply return the current multiplier and divisor.
- HWC2::Device::FrequencyScaler frequencyScaler;
- frequencyScaler.multiplier = data.readInt32();
- frequencyScaler.divisor = data.readInt32();
-
- if ((frequencyScaler.multiplier == 0) || (frequencyScaler.divisor == 0)) {
- frequencyScaler = getBE().mHwc->getDisplayFrequencyScaleParameters();
- reply->writeInt32(frequencyScaler.multiplier);
- reply->writeInt32(frequencyScaler.divisor);
- return NO_ERROR;
- }
-
- if ((frequencyScaler.multiplier == 1) && (frequencyScaler.divisor == 1)) {
- if (mUseScheduler) {
- mScheduler->enableHardwareVsync();
- } else {
- enableHardwareVsync();
- }
- } else {
- if (mUseScheduler) {
- mScheduler->disableHardwareVsync(true);
- } else {
- disableHardwareVsync(true);
- }
- }
- mPrimaryDispSync->scalePeriod(frequencyScaler);
- getBE().mHwc->setDisplayFrequencyScaleParameters(frequencyScaler);
-
- ATRACE_INT("PeriodMultiplier", frequencyScaler.multiplier);
- ATRACE_INT("PeriodDivisor", frequencyScaler.divisor);
-
- const hwc2_display_t hwcDisplayId = getBE().mHwc->getActiveConfig(
- DisplayDevice::DISPLAY_PRIMARY)->getDisplayId();
-
- onHotplugReceived(getBE().mComposerSequenceId,
- hwcDisplayId, HWC2::Connection::Disconnected);
- onHotplugReceived(getBE().mComposerSequenceId,
- hwcDisplayId, HWC2::Connection::Connected);
- frequencyScaler = getBE().mHwc->getDisplayFrequencyScaleParameters();
- reply->writeInt32(frequencyScaler.multiplier);
- reply->writeInt32(frequencyScaler.divisor);
-
- return NO_ERROR;
- }
// Is device color managed?
case 1030: {
reply->writeBool(useColorManagement);
@@ -5257,7 +5058,8 @@
};
status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken,
- sp<GraphicBuffer>* outBuffer, Rect sourceCrop,
+ sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
uint32_t reqWidth, uint32_t reqHeight,
bool useIdentityTransform,
ISurfaceComposer::Rotation rotation) {
@@ -5286,23 +5088,27 @@
}
}
- DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, renderAreaRotation);
+ DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
+ renderAreaRotation);
auto traverseLayers = std::bind(std::mem_fn(&SurfaceFlinger::traverseLayersInDisplay), this,
display, std::placeholders::_1);
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, useIdentityTransform);
+ return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
+ useIdentityTransform);
}
status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder,
- sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop,
+ sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace,
+ const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
float frameScale, bool childrenOnly) {
ATRACE_CALL();
class LayerRenderArea : public RenderArea {
public:
LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
- int32_t reqWidth, int32_t reqHeight, bool childrenOnly)
- : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR),
+ int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
+ bool childrenOnly)
+ : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace),
mLayer(layer),
mCrop(crop),
mNeedsFiltering(false),
@@ -5409,7 +5215,7 @@
reqHeight = 1;
}
- LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, childrenOnly);
+ LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly);
auto traverseLayers = [parent, childrenOnly](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -5421,19 +5227,22 @@
visitor(layer);
});
};
- return captureScreenCommon(renderArea, traverseLayers, outBuffer, false);
+ return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false);
}
status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
sp<GraphicBuffer>* outBuffer,
+ const ui::PixelFormat reqPixelFormat,
bool useIdentityTransform) {
ATRACE_CALL();
+ // TODO(b/116112787) Make buffer usage a parameter.
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
*outBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
- HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot");
+ static_cast<android_pixel_format>(reqPixelFormat), 1, usage,
+ "screenshot");
// This mutex protects syncFd and captureResult for communication of the return values from the
// main thread back to this Binder thread
@@ -5509,8 +5318,7 @@
const auto sourceCrop = renderArea.getSourceCrop();
const auto rotation = renderArea.getRotationFlags();
- // assume ColorMode::SRGB / RenderIntent::COLORIMETRIC
- engine.setOutputDataSpace(Dataspace::SRGB);
+ engine.setOutputDataSpace(renderArea.getReqDataSpace());
engine.setDisplayMaxLuminance(DisplayDevice::sDefaultMaxLumiance);
// make sure to clear all GL error flags
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f535c4e..8045246 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -433,10 +433,14 @@
virtual sp<IDisplayEventConnection> createDisplayEventConnection(
ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp);
virtual status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
- Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
- bool useIdentityTransform, ISurfaceComposer::Rotation rotation);
+ const ui::Dataspace reqDataspace,
+ const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+ uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+ ISurfaceComposer::Rotation rotation);
virtual status_t captureLayers(const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer,
- const Rect& sourceCrop, float frameScale, bool childrenOnly);
+ const ui::Dataspace reqDataspace,
+ const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
+ float frameScale, bool childrenOnly);
virtual status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
virtual status_t getDisplayConfigs(const sp<IBinder>& displayToken,
Vector<DisplayInfo>* configs);
@@ -586,7 +590,7 @@
void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
bool useIdentityTransform);
status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
- sp<GraphicBuffer>* outBuffer,
+ sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
bool useIdentityTransform);
status_t captureScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
@@ -689,7 +693,6 @@
* to prepare the hardware composer
*/
void prepareFrame(const sp<DisplayDevice>& display);
- void setUpHWComposer(const CompositionInfo& compositionInfo);
void doComposition(const sp<DisplayDevice>& display, bool repainEverything);
void doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything);
void doTracing(const char* where);
@@ -806,11 +809,6 @@
std::atomic<bool> mRepaintEverything{false};
- // helper methods
- void configureHwcCommonData(const CompositionInfo& compositionInfo) const;
- void configureDeviceComposition(const CompositionInfo& compositionInfo) const;
- void configureSidebandComposition(const CompositionInfo& compositionInfo) const;
-
// constant members (no synchronization needed for access)
nsecs_t mBootTime;
bool mGpuToCpuSupported;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 0bc1c0a..43fa262 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -24,6 +24,7 @@
#include <log/log.h>
#include <utils/String8.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
#include <algorithm>
@@ -82,7 +83,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- timeStats.totalFrames++;
+ mTimeStats.totalFrames++;
}
void TimeStats::incrementMissedFrames() {
@@ -91,7 +92,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- timeStats.missedFrames++;
+ mTimeStats.missedFrames++;
}
void TimeStats::incrementClientCompositionFrames() {
@@ -100,12 +101,12 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- timeStats.clientCompositionFrames++;
+ mTimeStats.clientCompositionFrames++;
}
-bool TimeStats::recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord) {
+bool TimeStats::recordReadyLocked(int32_t layerID, TimeRecord* timeRecord) {
if (!timeRecord->ready) {
- ALOGV("[%s]-[%" PRIu64 "]-presentFence is still not received", layerName.c_str(),
+ ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerID,
timeRecord->frameTime.frameNumber);
return false;
}
@@ -118,7 +119,7 @@
timeRecord->frameTime.acquireTime = timeRecord->acquireFence->getSignalTime();
timeRecord->acquireFence = nullptr;
} else {
- ALOGV("[%s]-[%" PRIu64 "]-acquireFence signal time is invalid", layerName.c_str(),
+ ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerID,
timeRecord->frameTime.frameNumber);
}
}
@@ -131,7 +132,7 @@
timeRecord->frameTime.presentTime = timeRecord->presentFence->getSignalTime();
timeRecord->presentFence = nullptr;
} else {
- ALOGV("[%s]-[%" PRIu64 "]-presentFence signal time invalid", layerName.c_str(),
+ ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerID,
timeRecord->frameTime.frameNumber);
}
}
@@ -145,14 +146,15 @@
return static_cast<int32_t>(delta);
}
+// This regular expression captures the following for instance:
+// StatusBar in StatusBar#0
+// com.appname in com.appname/com.appname.activity#0
+// com.appname in SurfaceView - com.appname/com.appname.activity#0
+static const std::regex packageNameRegex("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+");
+
static std::string getPackageName(const std::string& layerName) {
- // This regular expression captures the following for instance:
- // StatusBar in StatusBar#0
- // com.appname in com.appname/com.appname.activity#0
- // com.appname in SurfaceView - com.appname/com.appname.activity#0
- const std::regex re("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+");
std::smatch match;
- if (std::regex_match(layerName.begin(), layerName.end(), match, re)) {
+ if (std::regex_match(layerName.begin(), layerName.end(), match, packageNameRegex)) {
// There must be a match for group 1 otherwise the whole string is not
// matched and the above will return false
return match[1];
@@ -160,60 +162,61 @@
return "";
}
-void TimeStats::flushAvailableRecordsToStatsLocked(const std::string& layerName) {
+void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) {
ATRACE_CALL();
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
while (!timeRecords.empty()) {
- if (!recordReadyLocked(layerName, &timeRecords[0])) break;
- ALOGV("[%s]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerName.c_str(),
+ if (!recordReadyLocked(layerID, &timeRecords[0])) break;
+ ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID,
timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
if (prevTimeRecord.ready) {
- if (!timeStats.stats.count(layerName)) {
- timeStats.stats[layerName].layerName = layerName;
- timeStats.stats[layerName].packageName = getPackageName(layerName);
+ const std::string& layerName = layerRecord.layerName;
+ if (!mTimeStats.stats.count(layerName)) {
+ mTimeStats.stats[layerName].layerName = layerName;
+ mTimeStats.stats[layerName].packageName = getPackageName(layerName);
}
- TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timeStats.stats[layerName];
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
timeStatsLayer.totalFrames++;
timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
layerRecord.droppedFrames = 0;
const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime,
timeRecords[0].frameTime.acquireTime);
- ALOGV("[%s]-[%" PRIu64 "]-post2acquire[%d]", layerName.c_str(),
+ ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerID,
timeRecords[0].frameTime.frameNumber, postToAcquireMs);
timeStatsLayer.deltas["post2acquire"].insert(postToAcquireMs);
const int32_t postToPresentMs = msBetween(timeRecords[0].frameTime.postTime,
timeRecords[0].frameTime.presentTime);
- ALOGV("[%s]-[%" PRIu64 "]-post2present[%d]", layerName.c_str(),
+ ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerID,
timeRecords[0].frameTime.frameNumber, postToPresentMs);
timeStatsLayer.deltas["post2present"].insert(postToPresentMs);
const int32_t acquireToPresentMs = msBetween(timeRecords[0].frameTime.acquireTime,
timeRecords[0].frameTime.presentTime);
- ALOGV("[%s]-[%" PRIu64 "]-acquire2present[%d]", layerName.c_str(),
+ ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerID,
timeRecords[0].frameTime.frameNumber, acquireToPresentMs);
timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs);
const int32_t latchToPresentMs = msBetween(timeRecords[0].frameTime.latchTime,
timeRecords[0].frameTime.presentTime);
- ALOGV("[%s]-[%" PRIu64 "]-latch2present[%d]", layerName.c_str(),
+ ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerID,
timeRecords[0].frameTime.frameNumber, latchToPresentMs);
timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs);
const int32_t desiredToPresentMs = msBetween(timeRecords[0].frameTime.desiredTime,
timeRecords[0].frameTime.presentTime);
- ALOGV("[%s]-[%" PRIu64 "]-desired2present[%d]", layerName.c_str(),
+ ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerID,
timeRecords[0].frameTime.frameNumber, desiredToPresentMs);
timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs);
const int32_t presentToPresentMs = msBetween(prevTimeRecord.frameTime.presentTime,
timeRecords[0].frameTime.presentTime);
- ALOGV("[%s]-[%" PRIu64 "]-present2present[%d]", layerName.c_str(),
+ ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerID,
timeRecords[0].frameTime.frameNumber, presentToPresentMs);
timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
}
@@ -223,36 +226,48 @@
}
}
+// This regular expression captures the following layer names for instance:
+// 1) StatusBat#0
+// 2) NavigationBar#1
+// 3) co(m).*#0
+// 4) SurfaceView - co(m).*#0
+// Using [-\\s\t]+ for the conjunction part between SurfaceView and co(m).*
+// is a bit more robust in case there's a slight change.
+// The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases.
+static const std::regex layerNameRegex(
+ "(((SurfaceView[-\\s\\t]+)?com?\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+");
+
static bool layerNameIsValid(const std::string& layerName) {
- // This regular expression captures the following layer names for instance:
- // 1) StatusBat#0
- // 2) NavigationBar#1
- // 3) co(m).*#0
- // 4) SurfaceView - co(m).*#0
- // Using [-\\s\t]+ for the conjunction part between SurfaceView and co(m).*
- // is a bit more robust in case there's a slight change.
- // The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases.
- std::regex re("(((SurfaceView[-\\s\\t]+)?com?\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+");
- return std::regex_match(layerName.begin(), layerName.end(), re);
+ return std::regex_match(layerName.begin(), layerName.end(), layerNameRegex);
}
-void TimeStats::setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime) {
+void TimeStats::setLayerName(int32_t layerID, const std::string& layerName) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-PostTime[%" PRId64 "]", layerName.c_str(), frameNumber, postTime);
+ ALOGV("[%d]-[%s]", layerID, layerName.c_str());
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName) && !layerNameIsValid(layerName)) {
- return;
+ if (!mTimeStatsTracker.count(layerID) && layerNameIsValid(layerName)) {
+ mTimeStatsTracker[layerID].layerName = layerName;
}
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+}
+
+void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, nsecs_t postTime) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%d]-[%" PRIu64 "]-PostTime[%" PRId64 "]", layerID, frameNumber, postTime);
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
- ALOGV("[%s]-timeRecords is already at its maximum size[%zu]", layerName.c_str(),
- MAX_NUM_TIME_RECORDS);
- // TODO(zzyiwei): if this happens, there must be a present fence missing
- // or waitData is not in the correct position. Need to think out a
- // reasonable way to recover from this state.
+ ALOGE("[%d]-[%s]-timeRecords is already at its maximum size[%zu]. Please file a bug.",
+ layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
+ layerRecord.timeRecords.clear();
+ layerRecord.prevTimeRecord.ready = false;
+ layerRecord.waitData = -1;
return;
}
// For most media content, the acquireFence is invalid because the buffer is
@@ -272,84 +287,77 @@
layerRecord.waitData = layerRecord.timeRecords.size() - 1;
}
-void TimeStats::setLatchTime(const std::string& layerName, uint64_t frameNumber,
- nsecs_t latchTime) {
+void TimeStats::setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerName.c_str(), frameNumber, latchTime);
+ ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerID, frameNumber, latchTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
if (timeRecord.frameTime.frameNumber == frameNumber) {
timeRecord.frameTime.latchTime = latchTime;
}
}
-void TimeStats::setDesiredTime(const std::string& layerName, uint64_t frameNumber,
- nsecs_t desiredTime) {
+void TimeStats::setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerName.c_str(), frameNumber,
- desiredTime);
+ ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerID, frameNumber, desiredTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
if (timeRecord.frameTime.frameNumber == frameNumber) {
timeRecord.frameTime.desiredTime = desiredTime;
}
}
-void TimeStats::setAcquireTime(const std::string& layerName, uint64_t frameNumber,
- nsecs_t acquireTime) {
+void TimeStats::setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerName.c_str(), frameNumber,
- acquireTime);
+ ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerID, frameNumber, acquireTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
if (timeRecord.frameTime.frameNumber == frameNumber) {
timeRecord.frameTime.acquireTime = acquireTime;
}
}
-void TimeStats::setAcquireFence(const std::string& layerName, uint64_t frameNumber,
+void TimeStats::setAcquireFence(int32_t layerID, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+ ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerID, frameNumber,
acquireFence->getSignalTime());
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
if (timeRecord.frameTime.frameNumber == frameNumber) {
timeRecord.acquireFence = acquireFence;
}
}
-void TimeStats::setPresentTime(const std::string& layerName, uint64_t frameNumber,
- nsecs_t presentTime) {
+void TimeStats::setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerName.c_str(), frameNumber,
- presentTime);
+ ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerID, frameNumber, presentTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
if (timeRecord.frameTime.frameNumber == frameNumber) {
timeRecord.frameTime.presentTime = presentTime;
@@ -357,20 +365,20 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerName);
+ flushAvailableRecordsToStatsLocked(layerID);
}
-void TimeStats::setPresentFence(const std::string& layerName, uint64_t frameNumber,
+void TimeStats::setPresentFence(int32_t layerID, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
+ ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerID, frameNumber,
presentFence->getSignalTime());
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
if (timeRecord.frameTime.frameNumber == frameNumber) {
timeRecord.presentFence = presentFence;
@@ -378,45 +386,57 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerName);
+ flushAvailableRecordsToStatsLocked(layerID);
}
-void TimeStats::onDisconnect(const std::string& layerName) {
+void TimeStats::onDisconnect(int32_t layerID) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-onDisconnect", layerName.c_str());
+ ALOGV("[%d]-onDisconnect", layerID);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- flushAvailableRecordsToStatsLocked(layerName);
- timeStatsTracker.erase(layerName);
+ if (!mTimeStatsTracker.count(layerID)) return;
+ flushAvailableRecordsToStatsLocked(layerID);
+ mTimeStatsTracker.erase(layerID);
}
-void TimeStats::clearLayerRecord(const std::string& layerName) {
+void TimeStats::onDestroy(int32_t layerID) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-clearLayerRecord", layerName.c_str());
+ ALOGV("[%d]-onDestroy", layerID);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ flushAvailableRecordsToStatsLocked(layerID);
+ mTimeStatsTracker.erase(layerID);
+}
+
+void TimeStats::clearLayerRecord(int32_t layerID) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ ALOGV("[%d]-clearLayerRecord", layerID);
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
layerRecord.timeRecords.clear();
layerRecord.prevTimeRecord.ready = false;
layerRecord.waitData = -1;
layerRecord.droppedFrames = 0;
}
-void TimeStats::removeTimeRecord(const std::string& layerName, uint64_t frameNumber) {
+void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) {
if (!mEnabled.load()) return;
ATRACE_CALL();
- ALOGV("[%s]-[%" PRIu64 "]-removeTimeRecord", layerName.c_str(), frameNumber);
+ ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerID, frameNumber);
std::lock_guard<std::mutex> lock(mMutex);
- if (!timeStatsTracker.count(layerName)) return;
- LayerRecord& layerRecord = timeStatsTracker[layerName];
+ if (!mTimeStatsTracker.count(layerID)) return;
+ LayerRecord& layerRecord = mTimeStatsTracker[layerID];
size_t removeAt = 0;
for (const TimeRecord& record : layerRecord.timeRecords) {
if (record.frameTime.frameNumber == frameNumber) break;
@@ -430,6 +450,87 @@
layerRecord.droppedFrames++;
}
+void TimeStats::flushPowerTimeLocked() {
+ nsecs_t curTime = systemTime();
+ // elapsedTime is in milliseconds.
+ int64_t elapsedTime = (curTime - mPowerTime.prevTime) / 1000000;
+
+ switch (mPowerTime.powerMode) {
+ case HWC_POWER_MODE_NORMAL:
+ mTimeStats.displayOnTime += elapsedTime;
+ break;
+ case HWC_POWER_MODE_OFF:
+ case HWC_POWER_MODE_DOZE:
+ case HWC_POWER_MODE_DOZE_SUSPEND:
+ default:
+ break;
+ }
+
+ mPowerTime.prevTime = curTime;
+}
+
+void TimeStats::setPowerMode(int32_t powerMode) {
+ if (!mEnabled.load()) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mPowerTime.powerMode = powerMode;
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (powerMode == mPowerTime.powerMode) return;
+
+ flushPowerTimeLocked();
+ mPowerTime.powerMode = powerMode;
+}
+
+void TimeStats::flushAvailableGlobalRecordsToStatsLocked() {
+ ATRACE_CALL();
+
+ while (!mGlobalRecord.presentFences.empty()) {
+ const nsecs_t curPresentTime = mGlobalRecord.presentFences.front()->getSignalTime();
+ if (curPresentTime == Fence::SIGNAL_TIME_PENDING) break;
+
+ if (curPresentTime == Fence::SIGNAL_TIME_INVALID) {
+ ALOGE("GlobalPresentFence is invalid!");
+ mGlobalRecord.prevPresentTime = 0;
+ mGlobalRecord.presentFences.pop_front();
+ continue;
+ }
+
+ ALOGV("GlobalPresentFenceTime[%" PRId64 "]",
+ mGlobalRecord.presentFences.front()->getSignalTime());
+
+ const int32_t presentToPresentMs = msBetween(mGlobalRecord.prevPresentTime, curPresentTime);
+ ALOGV("Global present2present[%d]", presentToPresentMs);
+
+ mTimeStats.presentToPresent.insert(presentToPresentMs);
+ mGlobalRecord.prevPresentTime = curPresentTime;
+ mGlobalRecord.presentFences.pop_front();
+ }
+}
+
+void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (presentFence == nullptr) {
+ mGlobalRecord.prevPresentTime = 0;
+ return;
+ }
+
+ if (mGlobalRecord.presentFences.size() == MAX_NUM_TIME_RECORDS) {
+ // The front presentFence must be trapped in pending status in this
+ // case. Try dequeuing the front one to recover.
+ ALOGE("GlobalPresentFences is already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+ mGlobalRecord.prevPresentTime = 0;
+ mGlobalRecord.presentFences.pop_front();
+ }
+
+ mGlobalRecord.presentFences.emplace_back(presentFence);
+ flushAvailableGlobalRecordsToStatsLocked();
+}
+
void TimeStats::enable() {
if (mEnabled.load()) return;
@@ -438,7 +539,8 @@
std::lock_guard<std::mutex> lock(mMutex);
ALOGD("Enabled");
mEnabled.store(true);
- timeStats.statsStart = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
+ mPowerTime.prevTime = systemTime();
}
void TimeStats::disable() {
@@ -449,7 +551,7 @@
std::lock_guard<std::mutex> lock(mMutex);
ALOGD("Disabled");
mEnabled.store(false);
- timeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
}
void TimeStats::clear() {
@@ -457,12 +559,15 @@
std::lock_guard<std::mutex> lock(mMutex);
ALOGD("Cleared");
- timeStats.stats.clear();
- timeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
- timeStats.statsEnd = 0;
- timeStats.totalFrames = 0;
- timeStats.missedFrames = 0;
- timeStats.clientCompositionFrames = 0;
+ mTimeStats.stats.clear();
+ mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
+ mTimeStats.statsEnd = 0;
+ mTimeStats.totalFrames = 0;
+ mTimeStats.missedFrames = 0;
+ mTimeStats.clientCompositionFrames = 0;
+ mTimeStats.displayOnTime = 0;
+ mTimeStats.presentToPresent.hist.clear();
+ mPowerTime.prevTime = systemTime();
}
bool TimeStats::isEnabled() {
@@ -473,19 +578,21 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- if (timeStats.statsStart == 0) {
+ if (mTimeStats.statsStart == 0) {
return;
}
- timeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+
+ flushPowerTimeLocked();
if (asProto) {
ALOGD("Dumping TimeStats as proto");
- SFTimeStatsGlobalProto timeStatsProto = timeStats.toProto(maxLayers);
+ SFTimeStatsGlobalProto timeStatsProto = mTimeStats.toProto(maxLayers);
result.append(timeStatsProto.SerializeAsString().c_str(), timeStatsProto.ByteSize());
} else {
ALOGD("Dumping TimeStats as text");
- result.append(timeStats.toString(maxLayers).c_str());
+ result.append(mTimeStats.toString(maxLayers).c_str());
result.append("\n");
}
}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 5ab3934..d1e554c 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -19,6 +19,8 @@
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
+#include <hardware/hwcomposer_defs.h>
+
#include <ui/FenceTime.h>
#include <utils/String16.h>
@@ -57,6 +59,7 @@
};
struct LayerRecord {
+ std::string layerName;
// This is the index in timeRecords, at which the timestamps for that
// specific frame are still not fully received. This is not waiting for
// fences to signal, but rather waiting to receive those fences/timestamps.
@@ -66,6 +69,16 @@
std::deque<TimeRecord> timeRecords;
};
+ struct PowerTime {
+ int32_t powerMode = HWC_POWER_MODE_OFF;
+ nsecs_t prevTime = 0;
+ };
+
+ struct GlobalRecord {
+ nsecs_t prevPresentTime = 0;
+ std::deque<std::shared_ptr<FenceTime>> presentFences;
+ };
+
public:
static TimeStats& getInstance();
void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, String8& result);
@@ -73,27 +86,35 @@
void incrementMissedFrames();
void incrementClientCompositionFrames();
- void setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime);
- void setLatchTime(const std::string& layerName, uint64_t frameNumber, nsecs_t latchTime);
- void setDesiredTime(const std::string& layerName, uint64_t frameNumber, nsecs_t desiredTime);
- void setAcquireTime(const std::string& layerName, uint64_t frameNumber, nsecs_t acquireTime);
- void setAcquireFence(const std::string& layerName, uint64_t frameNumber,
+ void setLayerName(int32_t layerID, const std::string& layerName);
+ void setPostTime(int32_t layerID, uint64_t frameNumber, nsecs_t postTime);
+ void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime);
+ void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime);
+ void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime);
+ void setAcquireFence(int32_t layerID, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence);
- void setPresentTime(const std::string& layerName, uint64_t frameNumber, nsecs_t presentTime);
- void setPresentFence(const std::string& layerName, uint64_t frameNumber,
+ void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime);
+ void setPresentFence(int32_t layerID, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence);
// On producer disconnect with BufferQueue.
- void onDisconnect(const std::string& layerName);
+ void onDisconnect(int32_t layerID);
+ // On layer tear down.
+ void onDestroy(int32_t layerID);
// When SF is cleaning up the queue, clear the LayerRecord as well.
- void clearLayerRecord(const std::string& layerName);
+ void clearLayerRecord(int32_t layerID);
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
- void removeTimeRecord(const std::string& layerName, uint64_t frameNumber);
+ void removeTimeRecord(int32_t layerID, uint64_t frameNumber);
+
+ void setPowerMode(int32_t powerMode);
+ void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence);
private:
TimeStats() = default;
- bool recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord);
- void flushAvailableRecordsToStatsLocked(const std::string& layerName);
+ bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord);
+ void flushAvailableRecordsToStatsLocked(int32_t layerID);
+ void flushPowerTimeLocked();
+ void flushAvailableGlobalRecordsToStatsLocked();
void enable();
void disable();
@@ -103,8 +124,11 @@
std::atomic<bool> mEnabled = false;
std::mutex mMutex;
- TimeStatsHelper::TimeStatsGlobal timeStats;
- std::unordered_map<std::string, LayerRecord> timeStatsTracker;
+ TimeStatsHelper::TimeStatsGlobal mTimeStats;
+ // Hashmap for LayerRecord with layerID as the hash key
+ std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker;
+ PowerTime mPowerTime;
+ GlobalRecord mGlobalRecord;
};
} // namespace android
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index b7b2778..c701224 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -49,7 +49,7 @@
float TimeStatsHelper::Histogram::averageTime() const {
int64_t ret = 0;
int64_t count = 0;
- for (auto& ele : hist) {
+ for (const auto& ele : hist) {
count += ele.second;
ret += ele.first * ele.second;
}
@@ -73,13 +73,13 @@
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
- auto iter = deltas.find("present2present");
+ const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
}
- for (auto& ele : deltas) {
+ for (const auto& ele : deltas) {
StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str());
- StringAppendF(&result, "%s", ele.second.toString().c_str());
+ result.append(ele.second.toString());
}
return result;
@@ -92,9 +92,12 @@
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "missedFrames = %d\n", missedFrames);
StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
+ StringAppendF(&result, "displayOnTime = %lld ms\n", static_cast<long long int>(displayOnTime));
+ StringAppendF(&result, "presentToPresent histogram is as below:\n");
+ result.append(presentToPresent.toString());
const auto dumpStats = generateDumpStats(maxLayers);
- for (auto& ele : dumpStats) {
- StringAppendF(&result, "%s", ele->toString().c_str());
+ for (const auto& ele : dumpStats) {
+ result.append(ele->toString());
}
return result;
@@ -106,10 +109,10 @@
layerProto.set_package_name(packageName);
layerProto.set_total_frames(totalFrames);
layerProto.set_dropped_frames(droppedFrames);
- for (auto& ele : deltas) {
+ for (const auto& ele : deltas) {
SFTimeStatsDeltaProto* deltaProto = layerProto.add_deltas();
deltaProto->set_delta_name(ele.first);
- for (auto& histEle : ele.second.hist) {
+ for (const auto& histEle : ele.second.hist) {
SFTimeStatsHistogramBucketProto* histProto = deltaProto->add_histograms();
histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
@@ -126,8 +129,14 @@
globalProto.set_total_frames(totalFrames);
globalProto.set_missed_frames(missedFrames);
globalProto.set_client_composition_frames(clientCompositionFrames);
+ globalProto.set_display_on_time(displayOnTime);
+ for (const auto& histEle : presentToPresent.hist) {
+ SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present();
+ histProto->set_time_millis(histEle.first);
+ histProto->set_frame_count(histEle.second);
+ }
const auto dumpStats = generateDumpStats(maxLayers);
- for (auto& ele : dumpStats) {
+ for (const auto& ele : dumpStats) {
SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
layerProto->CopyFrom(ele->toProto());
}
@@ -137,7 +146,7 @@
std::vector<TimeStatsHelper::TimeStatsLayer const*>
TimeStatsHelper::TimeStatsGlobal::generateDumpStats(std::optional<uint32_t> maxLayers) const {
std::vector<TimeStatsLayer const*> dumpStats;
- for (auto& ele : stats) {
+ for (const auto& ele : stats) {
dumpStats.push_back(&ele.second);
}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 99c891b..7d0fe79 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -57,6 +57,8 @@
int32_t totalFrames = 0;
int32_t missedFrames = 0;
int32_t clientCompositionFrames = 0;
+ int64_t displayOnTime = 0;
+ Histogram presentToPresent;
std::unordered_map<std::string, TimeStatsLayer> stats;
std::string toString(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index b8f38c2..377612a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -20,13 +20,12 @@
option optimize_for = LITE_RUNTIME;
-// //depot/google3/java/com/google/android/apps/graphics/stats/proto/
+// //depot/google3/wireless/android/graphics/surfaceflingerstats/proto/
// timestats.proto is based on this proto. Please only make valid protobuf
-// changes to these messages, and keep the other file in sync per Android
-// release. Please also do not include "option optimize_for = LITE_RUNTIME;" at
-// google3 side.
+// changes to these messages, and keep google3 side proto messages in sync if
+// the end to end pipeline needs to be updated.
-// Next tag: 7
+// Next tag: 9
message SFTimeStatsGlobalProto {
// The stats start time in UTC as seconds since January 1, 1970
optional int64 stats_start = 1;
@@ -38,6 +37,10 @@
optional int32 missed_frames = 4;
// Total frames fallback to client composition.
optional int32 client_composition_frames = 5;
+ // Primary display on time in milliseconds.
+ optional int64 display_on_time = 7;
+ // Present to present histogram.
+ repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
// Stats per layer. Apps could have multiple layers.
repeated SFTimeStatsLayerProto stats = 6;
}
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 9e762f0..a73ec6c 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -267,8 +267,9 @@
sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
std::function<status_t()> condition = [=]() {
sp<GraphicBuffer> outBuffer;
- return ScreenshotClient::capture(display, Rect(), 0 /*reqWidth*/, 0 /*reqHeight*/, false,
- ROTATION, &outBuffer);
+ return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB,
+ ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/,
+ 0 /*reqHeight*/, false, ROTATION, &outBuffer);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
@@ -278,8 +279,9 @@
sp<GraphicBuffer> outBuffer;
std::function<status_t()> condition = [=]() {
sp<GraphicBuffer> outBuffer;
- return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(), Rect(), FRAME_SCALE,
- &outBuffer);
+ return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+ Rect(), FRAME_SCALE, &outBuffer);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 4e403b7..1352df5 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -42,6 +42,7 @@
namespace {
using testing::_;
+using testing::AtLeast;
using testing::ByMove;
using testing::DoAll;
using testing::IsNull;
@@ -189,7 +190,8 @@
constexpr bool forSystem = true;
DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT, ui::Transform::ROT_0);
+ DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB,
+ ui::Transform::ROT_0);
auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
return mFlinger.traverseLayersInDisplay(mDisplay, visitor);
@@ -287,7 +289,7 @@
EXPECT_CALL(*test->mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd())));
EXPECT_CALL(*test->mRenderEngine, finish()).WillOnce(Return(true));
- EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(ui::Dataspace::SRGB)).Times(1);
+ EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(_)).Times(1);
EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE))
.Times(1);
// This expectation retires on saturation as setViewportAndProjection is
@@ -514,7 +516,7 @@
// expectations are for appears to make an extra call to them.
// TODO: Investigate this extra call
EXPECT_CALL(*test->mComposer, setLayerTransform(HWC_DISPLAY, HWC_LAYER, DEFAULT_TRANSFORM))
- .Times(1)
+ .Times(AtLeast(1))
.RetiresOnSaturation();
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index b474e42..d32627a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -22,6 +22,7 @@
#include <log/log.h>
+#include <ui/DebugUtils.h>
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockDisplaySurface.h"
@@ -997,6 +998,93 @@
}
/* ------------------------------------------------------------------------
+ * DisplayDevice::GetBestColorMode
+ */
+class GetBestColorModeTest : public DisplayTransactionTest {
+public:
+ GetBestColorModeTest()
+ : DisplayTransactionTest(),
+ mInjector(FakeDisplayDeviceInjector(mFlinger, DisplayDevice::DISPLAY_PRIMARY, 0)) {}
+
+ void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
+
+ void addHwcColorModesMapping(ui::ColorMode colorMode,
+ std::vector<ui::RenderIntent> renderIntents) {
+ mHwcColorModes[colorMode] = renderIntents;
+ }
+
+ void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
+
+ void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
+
+ void getBestColorMode() {
+ mInjector.setHwcColorModes(mHwcColorModes);
+ mInjector.setHasWideColorGamut(mHasWideColorGamut);
+ auto displayDevice = mInjector.inject();
+
+ displayDevice->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
+ &mOutColorMode, &mOutRenderIntent);
+ }
+
+ ui::Dataspace mOutDataspace;
+ ui::ColorMode mOutColorMode;
+ ui::RenderIntent mOutRenderIntent;
+
+private:
+ ui::Dataspace mInputDataspace;
+ ui::RenderIntent mInputRenderIntent;
+ bool mHasWideColorGamut = false;
+ std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
+ FakeDisplayDeviceInjector mInjector;
+};
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
+ addHwcColorModesMapping(ui::ColorMode::SRGB,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::SRGB, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ addHwcColorModesMapping(ui::ColorMode::SRGB,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+/* ------------------------------------------------------------------------
* SurfaceFlinger::setupNewDisplayDeviceInternal
*/
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 2046439..62afde9 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -369,6 +369,18 @@
return *this;
}
+ auto& setHwcColorModes(
+ const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>
+ hwcColorModes) {
+ mCreationArgs.hwcColorModes = hwcColorModes;
+ return *this;
+ }
+
+ auto& setHasWideColorGamut(bool hasWideColorGamut) {
+ mCreationArgs.hasWideColorGamut = hasWideColorGamut;
+ return *this;
+ }
+
sp<DisplayDevice> inject() {
DisplayDeviceState state;
state.type = mCreationArgs.type;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index ecf3181..c0395c0 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -30,10 +30,10 @@
using android::hardware::graphics::common::V1_0::ColorTransform;
using android::hardware::graphics::common::V1_0::Hdr;
using android::hardware::graphics::common::V1_0::Transform;
-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::composer::V2_1::Config;
using android::hardware::graphics::composer::V2_1::Display;
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
index 495b3f2..34e71cb 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
@@ -35,7 +35,6 @@
MOCK_METHOD1(addResyncSample, bool(nsecs_t));
MOCK_METHOD0(endResync, void());
MOCK_METHOD1(setPeriod, void(nsecs_t));
- MOCK_METHOD1(scalePeriod, void(HWC2::Device::FrequencyScaler));
MOCK_METHOD0(getPeriod, nsecs_t());
MOCK_METHOD1(setRefreshSkipCount, void(int));
MOCK_METHOD3(addEventListener, status_t(const char*, nsecs_t, Callback*));
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
index 6813cda..afca63a 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
@@ -17,13 +17,15 @@
#pragma once
#include <gmock/gmock.h>
-
+#include <renderengine/DisplaySettings.h>
#include <renderengine/Framebuffer.h>
#include <renderengine/Image.h>
+#include <renderengine/LayerSettings.h>
#include <renderengine/Mesh.h>
#include <renderengine/RenderEngine.h>
#include <renderengine/Surface.h>
#include <renderengine/Texture.h>
+#include <ui/GraphicBuffer.h>
namespace android {
namespace renderengine {
@@ -75,6 +77,9 @@
MOCK_METHOD1(drawMesh, void(const Mesh&));
MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
+ MOCK_CONST_METHOD4(drawLayers,
+ status_t(const DisplaySettings&, const std::vector<LayerSettings>&,
+ ANativeWindowBuffer* const, base::unique_fd*));
};
class Surface : public renderengine::Surface {
diff --git a/services/vr/bufferhubd/IBufferHub.cpp b/services/vr/bufferhubd/IBufferHub.cpp
index 9d5b91a..2f39e41 100644
--- a/services/vr/bufferhubd/IBufferHub.cpp
+++ b/services/vr/bufferhubd/IBufferHub.cpp
@@ -4,14 +4,69 @@
namespace android {
namespace dvr {
+class BpBufferHub : public BpInterface<IBufferHub> {
+ public:
+ explicit BpBufferHub(const sp<IBinder>& impl)
+ : BpInterface<IBufferHub>(impl) {}
+
+ sp<IBufferClient> createBuffer(uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t format,
+ uint64_t usage,
+ uint64_t user_metadata_size) override;
+};
+
IMPLEMENT_META_INTERFACE(BufferHub, "android.dvr.IBufferHub");
+// Transaction code
+enum {
+ CREATE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+sp<IBufferClient> BpBufferHub::createBuffer(uint32_t width, uint32_t height,
+ uint32_t layer_count,
+ uint32_t format, uint64_t usage,
+ uint64_t user_metadata_size) {
+ Parcel data, reply;
+ status_t ret = NO_ERROR;
+ ret |= data.writeInterfaceToken(IBufferHub::getInterfaceDescriptor());
+ ret |= data.writeUint32(width);
+ ret |= data.writeUint32(height);
+ ret |= data.writeUint32(layer_count);
+ ret |= data.writeUint32(format);
+ ret |= data.writeUint64(usage);
+ ret |= data.writeUint64(user_metadata_size);
+
+ if (ret != NO_ERROR) {
+ ALOGE("BpBufferHub::createBuffer: failed to write into parcel");
+ return nullptr;
+ }
+
+ ret = remote()->transact(CREATE_BUFFER, data, &reply);
+ if (ret == NO_ERROR) {
+ return interface_cast<IBufferClient>(reply.readStrongBinder());
+ } else {
+ ALOGE("BpBufferHub::createBuffer: failed to transact; errno=%d", ret);
+ return nullptr;
+ }
+}
+
status_t BnBufferHub::onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags) {
switch (code) {
+ case CREATE_BUFFER: {
+ CHECK_INTERFACE(IBufferHub, data, reply);
+ uint32_t width = data.readUint32();
+ uint32_t height = data.readUint32();
+ uint32_t layer_count = data.readUint32();
+ uint32_t format = data.readUint32();
+ uint64_t usage = data.readUint64();
+ uint64_t user_metadata_size = data.readUint64();
+ sp<IBufferClient> ret = createBuffer(width, height, layer_count, format,
+ usage, user_metadata_size);
+ return reply->writeStrongBinder(IInterface::asBinder(ret));
+ } break;
default:
- // Should not reach
- ALOGE("onTransact(): unknown code %u received!", code);
+ // Should not reach except binder defined transactions such as dumpsys
return BBinder::onTransact(code, data, reply, flags);
}
}
diff --git a/services/vr/bufferhubd/buffer_channel.cpp b/services/vr/bufferhubd/buffer_channel.cpp
index dcc6ea4..ee85746 100644
--- a/services/vr/bufferhubd/buffer_channel.cpp
+++ b/services/vr/bufferhubd/buffer_channel.cpp
@@ -1,3 +1,4 @@
+#include <errno.h>
#include <private/dvr/buffer_channel.h>
#include <private/dvr/producer_channel.h>
@@ -16,9 +17,8 @@
size_t user_metadata_size)
: BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType),
buffer_node_(
- std::make_shared<BufferNode>(std::move(buffer), user_metadata_size)),
- buffer_state_bit_(BufferHubDefs::FindFirstClearedBit()) {
- buffer_node_->set_buffer_state_bit(buffer_state_bit_);
+ std::make_shared<BufferNode>(std::move(buffer), user_metadata_size)) {
+ buffer_state_bit_ = buffer_node_->AddNewActiveClientsBitToMask();
}
BufferChannel::BufferChannel(BufferHubService* service, int buffer_id,
@@ -27,24 +27,28 @@
uint64_t usage, size_t user_metadata_size)
: BufferHubChannel(service, buffer_id, buffer_id, kDetachedBufferType),
buffer_node_(std::make_shared<BufferNode>(
- width, height, layer_count, format, usage, user_metadata_size)),
- buffer_state_bit_(BufferHubDefs::FindFirstClearedBit()) {
- buffer_node_->set_buffer_state_bit(buffer_state_bit_);
+ width, height, layer_count, format, usage, user_metadata_size)) {
+ buffer_state_bit_ = buffer_node_->AddNewActiveClientsBitToMask();
}
BufferChannel::BufferChannel(BufferHubService* service, int buffer_id,
int channel_id,
- std::shared_ptr<BufferNode> buffer_node,
- uint64_t buffer_state_bit)
+ std::shared_ptr<BufferNode> buffer_node)
: BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType),
- buffer_node_(buffer_node),
- buffer_state_bit_(buffer_state_bit) {
- buffer_node_->set_buffer_state_bit(buffer_state_bit_);
+ buffer_node_(buffer_node) {
+ buffer_state_bit_ = buffer_node_->AddNewActiveClientsBitToMask();
+ if (buffer_state_bit_ == 0ULL) {
+ ALOGE("BufferChannel::BufferChannel: %s", strerror(errno));
+ buffer_node_ = nullptr;
+ }
}
BufferChannel::~BufferChannel() {
ALOGD_IF(TRACE, "BufferChannel::~BufferChannel: channel_id=%d buffer_id=%d.",
channel_id(), buffer_id());
+ if (buffer_state_bit_ != 0ULL) {
+ buffer_node_->RemoveClientsBitFromMask(buffer_state_bit_);
+ }
Hangup();
}
@@ -74,11 +78,6 @@
*this, &BufferChannel::OnDuplicate, message);
return true;
- case DetachedBufferRPC::Promote::Opcode:
- DispatchRemoteMethod<DetachedBufferRPC::Promote>(
- *this, &BufferChannel::OnPromote, message);
- return true;
-
default:
return false;
}
@@ -106,38 +105,22 @@
/*released_fence_fd=*/BorrowedHandle{}};
}
-Status<RemoteChannelHandle> BufferChannel::OnDuplicate(
- Message& message) {
+Status<RemoteChannelHandle> BufferChannel::OnDuplicate(Message& message) {
ATRACE_NAME("BufferChannel::OnDuplicate");
- ALOGD_IF(TRACE, "BufferChannel::OnDuplicate: buffer=%d.",
- buffer_id());
+ ALOGD_IF(TRACE, "BufferChannel::OnDuplicate: buffer=%d.", buffer_id());
int channel_id;
auto status = message.PushChannel(0, nullptr, &channel_id);
- if (!status) {
- ALOGE(
- "BufferChannel::OnDuplicate: Failed to push buffer channel: %s",
- status.GetErrorMessage().c_str());
+ if (!status.ok()) {
+ ALOGE("BufferChannel::OnDuplicate: Failed to push buffer channel: %s",
+ status.GetErrorMessage().c_str());
return ErrorStatus(ENOMEM);
}
- // Try find the next buffer state bit which has not been claimed by any
- // other buffers yet.
- uint64_t buffer_state_bit =
- BufferHubDefs::FindNextClearedBit(buffer_node_->active_buffer_bit_mask() |
- BufferHubDefs::kProducerStateBit);
- if (buffer_state_bit == 0ULL) {
- ALOGE(
- "BufferChannel::OnDuplicate: reached the maximum mumber of channels "
- "per buffer node: 63.");
- return ErrorStatus(E2BIG);
- }
-
- auto channel =
- std::shared_ptr<BufferChannel>(new BufferChannel(
- service(), buffer_id(), channel_id, buffer_node_, buffer_state_bit));
- if (!channel) {
- ALOGE("BufferChannel::OnDuplicate: Invalid buffer.");
+ auto channel = std::shared_ptr<BufferChannel>(
+ new BufferChannel(service(), buffer_id(), channel_id, buffer_node_));
+ if (!channel->IsValid()) {
+ ALOGE("BufferChannel::OnDuplicate: Invalid buffer. %s", strerror(errno));
return ErrorStatus(EINVAL);
}
@@ -154,69 +137,5 @@
return status;
}
-Status<RemoteChannelHandle> BufferChannel::OnPromote(
- Message& message) {
- ATRACE_NAME("BufferChannel::OnPromote");
- ALOGD_IF(TRACE, "BufferChannel::OnPromote: buffer_id=%d", buffer_id());
-
- // Check whether this is the channel exclusive owner of the buffer_node_.
- if (buffer_state_bit_ != buffer_node_->active_buffer_bit_mask()) {
- ALOGE(
- "BufferChannel::OnPromote: Cannot promote this BufferChannel as its "
- "BufferNode is shared between multiple channels. This channel's state "
- "bit=0x%" PRIx64 ", acitve_buffer_bit_mask=0x%" PRIx64 ".",
- buffer_state_bit_, buffer_node_->active_buffer_bit_mask());
- return ErrorStatus(EINVAL);
- }
-
- // Note that the new ProducerChannel will have different channel_id, but
- // inherits the buffer_id from the DetachedBuffer.
- int channel_id;
- auto status = message.PushChannel(0, nullptr, &channel_id);
- if (!status) {
- ALOGE(
- "BufferChannel::OnPromote: Failed to push ProducerChannel: %s.",
- status.GetErrorMessage().c_str());
- return ErrorStatus(ENOMEM);
- }
-
- IonBuffer buffer = std::move(buffer_node_->buffer());
- IonBuffer metadata_buffer;
- if (int ret = metadata_buffer.Alloc(buffer_node_->metadata().metadata_size(),
- /*height=*/1,
- /*layer_count=*/1,
- BufferHubDefs::kMetadataFormat,
- BufferHubDefs::kMetadataUsage)) {
- ALOGE("BufferChannel::OnPromote: Failed to allocate metadata: %s",
- strerror(-ret));
- return ErrorStatus(EINVAL);
- }
-
- size_t user_metadata_size = buffer_node_->user_metadata_size();
-
- std::unique_ptr<ProducerChannel> channel = ProducerChannel::Create(
- service(), buffer_id(), channel_id, std::move(buffer),
- std::move(metadata_buffer), user_metadata_size);
- if (!channel) {
- ALOGE(
- "BufferChannel::OnPromote: Failed to create ProducerChannel from a "
- "BufferChannel, buffer_id=%d.",
- buffer_id());
- }
-
- const auto channel_status =
- service()->SetChannel(channel_id, std::move(channel));
- if (!channel_status) {
- // Technically, this should never fail, as we just pushed the channel. Note
- // that LOG_FATAL will be stripped out in non-debug build.
- LOG_FATAL(
- "BufferChannel::OnPromote: Failed to set new producer buffer channel: "
- "%s.",
- channel_status.GetErrorMessage().c_str());
- }
-
- return status;
-}
-
} // namespace dvr
} // namespace android
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index 15391da..6421a0b 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -265,14 +265,6 @@
SetChannel(channel->channel_id(), nullptr);
return {};
- case DetachedBufferRPC::Promote::Opcode:
- // In addition to the message handler in the BufferChannel's
- // HandleMessage method, we also need to invalid the channel. Note that
- // this has to be done after HandleMessage returns to make sure the IPC
- // request has went back to the client first.
- SetChannel(channel->channel_id(), nullptr);
- return {};
-
default:
return DefaultHandleMessage(message);
}
diff --git a/services/vr/bufferhubd/buffer_hub_binder.cpp b/services/vr/bufferhubd/buffer_hub_binder.cpp
index b507717..f8a9758 100644
--- a/services/vr/bufferhubd/buffer_hub_binder.cpp
+++ b/services/vr/bufferhubd/buffer_hub_binder.cpp
@@ -5,6 +5,7 @@
#include <binder/ProcessState.h>
#include <log/log.h>
#include <private/dvr/buffer_hub_binder.h>
+#include <private/dvr/buffer_node.h>
namespace android {
namespace dvr {
@@ -13,7 +14,7 @@
const std::shared_ptr<BufferHubService>& pdx_service) {
IPCThreadState::self()->disableBackgroundScheduling(true);
- BufferHubBinderService* service = new BufferHubBinderService();
+ sp<BufferHubBinderService> service = new BufferHubBinderService();
service->pdx_service_ = pdx_service;
// Not using BinderService::publish because need to get an instance of this
@@ -45,8 +46,14 @@
"Input arguments are ignored.\n");
}
- // TODO(b/116526156): output real data in this class once we have it
+ fprintf(out, "Binder service:\n");
+ // Active buffers
+ fprintf(out, "Active BufferClients: %zu\n", client_list_.size());
+ // TODO(b/117790952): print buffer information after BufferNode has it
+ // TODO(b/116526156): print more information once we have them
+
if (pdx_service_) {
+ fprintf(out, "\nPDX service:\n");
// BufferHubService::Dumpstate(size_t) is not actually using the param
// So just using 0 as the length
fprintf(out, "%s", pdx_service_->DumpState(0).c_str());
@@ -58,23 +65,17 @@
return NO_ERROR;
}
-sp<IBufferHub> BufferHubBinderService::getServiceProxy() {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> service = sm->checkService(String16(getServiceName()));
+sp<IBufferClient> BufferHubBinderService::createBuffer(
+ uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format,
+ uint64_t usage, uint64_t user_metadata_size) {
+ std::shared_ptr<BufferNode> node = std::make_shared<BufferNode>(
+ width, height, layer_count, format, usage, user_metadata_size);
- if (service == nullptr) {
- ALOGE("getServiceProxy(): %s binder service not found!", getServiceName());
- return nullptr;
- }
+ sp<BufferClient> client = new BufferClient(node);
+ // Add it to list for bookkeeping and dumpsys.
+ client_list_.push_back(client);
- sp<IBufferHub> ret = interface_cast<IBufferHub>(service);
- if (ret == nullptr) {
- ALOGE("getServiceProxy(): %s binder service type casting error!",
- getServiceName());
- return nullptr;
- }
-
- return ret;
+ return client;
}
} // namespace dvr
diff --git a/services/vr/bufferhubd/buffer_node.cpp b/services/vr/bufferhubd/buffer_node.cpp
index 782b9c2..bedec6f 100644
--- a/services/vr/bufferhubd/buffer_node.cpp
+++ b/services/vr/bufferhubd/buffer_node.cpp
@@ -1,12 +1,24 @@
+#include <errno.h>
#include <private/dvr/buffer_hub_defs.h>
#include <private/dvr/buffer_node.h>
namespace android {
namespace dvr {
+void BufferNode::InitializeMetadata() {
+ // Using placement new here to reuse shared memory instead of new allocation
+ // Initialize the atomic variables to zero.
+ BufferHubDefs::MetadataHeader* metadata_header = metadata_.metadata_header();
+ buffer_state_ = new (&metadata_header->buffer_state) std::atomic<uint64_t>(0);
+ fence_state_ = new (&metadata_header->fence_state) std::atomic<uint64_t>(0);
+ active_clients_bit_mask_ =
+ new (&metadata_header->active_clients_bit_mask) std::atomic<uint64_t>(0);
+}
+
BufferNode::BufferNode(IonBuffer buffer, size_t user_metadata_size)
: buffer_(std::move(buffer)) {
metadata_ = BufferHubMetadata::Create(user_metadata_size);
+ InitializeMetadata();
}
// Allocates a new BufferNode.
@@ -22,6 +34,37 @@
}
metadata_ = BufferHubMetadata::Create(user_metadata_size);
+ InitializeMetadata();
+}
+
+uint64_t BufferNode::GetActiveClientsBitMask() const {
+ return active_clients_bit_mask_->load(std::memory_order_acquire);
+}
+
+uint64_t BufferNode::AddNewActiveClientsBitToMask() {
+ uint64_t current_active_clients_bit_mask = GetActiveClientsBitMask();
+ uint64_t buffer_state_bit = 0ULL;
+ uint64_t updated_active_clients_bit_mask = 0ULL;
+ do {
+ buffer_state_bit =
+ BufferHubDefs::FindNextClearedBit(current_active_clients_bit_mask);
+ if (buffer_state_bit == 0ULL) {
+ ALOGE(
+ "BufferNode::AddNewActiveClientsBitToMask: reached the maximum "
+ "mumber of channels per buffer node: 32.");
+ errno = E2BIG;
+ return 0ULL;
+ }
+ updated_active_clients_bit_mask =
+ current_active_clients_bit_mask | buffer_state_bit;
+ } while (!(active_clients_bit_mask_->compare_exchange_weak(
+ current_active_clients_bit_mask, updated_active_clients_bit_mask,
+ std::memory_order_acq_rel, std::memory_order_acquire)));
+ return buffer_state_bit;
+}
+
+void BufferNode::RemoveClientsBitFromMask(const uint64_t& value) {
+ active_clients_bit_mask_->fetch_and(~value);
}
} // namespace dvr
diff --git a/services/vr/bufferhubd/include/private/dvr/IBufferHub.h b/services/vr/bufferhubd/include/private/dvr/IBufferHub.h
index 266ae88..bd5f9cf 100644
--- a/services/vr/bufferhubd/include/private/dvr/IBufferHub.h
+++ b/services/vr/bufferhubd/include/private/dvr/IBufferHub.h
@@ -3,6 +3,7 @@
#include <binder/IInterface.h>
#include <binder/Parcel.h>
+#include <private/dvr/IBufferClient.h>
namespace android {
namespace dvr {
@@ -10,6 +11,12 @@
class IBufferHub : public IInterface {
public:
DECLARE_META_INTERFACE(BufferHub);
+
+ static const char* getServiceName() { return "bufferhubd"; }
+ virtual sp<IBufferClient> createBuffer(uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t format,
+ uint64_t usage,
+ uint64_t user_metadata_size) = 0;
};
class BnBufferHub : public BnInterface<IBufferHub> {
@@ -18,13 +25,7 @@
uint32_t flags = 0);
};
-class BpBufferHub : public BpInterface<IBufferHub> {
- public:
- explicit BpBufferHub(const sp<IBinder>& impl)
- : BpInterface<IBufferHub>(impl) {}
-};
-
} // namespace dvr
} // namespace android
-#endif
\ No newline at end of file
+#endif // ANDROID_DVR_IBUFFERHUB_H
\ No newline at end of file
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_channel.h b/services/vr/bufferhubd/include/private/dvr/buffer_channel.h
index 1697251..e9bdb37 100644
--- a/services/vr/bufferhubd/include/private/dvr/buffer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_channel.h
@@ -42,21 +42,20 @@
uint32_t height, uint32_t layer_count, uint32_t format,
uint64_t usage, size_t user_metadata_size);
- // Creates a detached buffer from an existing BufferNode.
+ // Creates a detached buffer from an existing BufferNode. This method is used
+ // in OnDuplicate method.
BufferChannel(BufferHubService* service, int buffer_id, int channel_id,
- std::shared_ptr<BufferNode> buffer_node,
- uint64_t buffer_state_bit);
+ std::shared_ptr<BufferNode> buffer_node);
pdx::Status<BufferTraits<pdx::BorrowedHandle>> OnImport(
pdx::Message& message);
pdx::Status<pdx::RemoteChannelHandle> OnDuplicate(pdx::Message& message);
- pdx::Status<pdx::RemoteChannelHandle> OnPromote(pdx::Message& message);
// The concrete implementation of the Buffer object.
- std::shared_ptr<BufferNode> buffer_node_;
+ std::shared_ptr<BufferNode> buffer_node_ = nullptr;
// The state bit of this buffer. Must be one the lower 63 bits.
- uint64_t buffer_state_bit_;
+ uint64_t buffer_state_bit_ = 0ULL;
};
} // namespace dvr
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_client.h b/services/vr/bufferhubd/include/private/dvr/buffer_client.h
new file mode 100644
index 0000000..20d51ee
--- /dev/null
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_client.h
@@ -0,0 +1,29 @@
+#ifndef ANDROID_DVR_BUFFERCLIENT_H
+#define ANDROID_DVR_BUFFERCLIENT_H
+
+#include <private/dvr/IBufferClient.h>
+#include <private/dvr/buffer_node.h>
+
+namespace android {
+namespace dvr {
+
+class BufferClient : public BnBufferClient {
+ public:
+ // Creates a server-side buffer client from an existing BufferNode. Note that
+ // this funciton takes ownership of the shared_ptr.
+ explicit BufferClient(std::shared_ptr<BufferNode> node)
+ : buffer_node_(std::move(node)){};
+
+ // Binder IPC functions
+ bool isValid() override {
+ return buffer_node_ ? buffer_node_->IsValid() : false;
+ };
+
+ private:
+ std::shared_ptr<BufferNode> buffer_node_;
+};
+
+} // namespace dvr
+} // namespace android
+
+#endif // ANDROID_DVR_IBUFFERCLIENT_H
\ No newline at end of file
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h
index e266ff8..9064d87 100644
--- a/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h
@@ -1,8 +1,11 @@
#ifndef ANDROID_DVR_BUFFER_HUB_BINDER_H
#define ANDROID_DVR_BUFFER_HUB_BINDER_H
+#include <vector>
+
#include <binder/BinderService.h>
#include <private/dvr/IBufferHub.h>
+#include <private/dvr/buffer_client.h>
#include <private/dvr/buffer_hub.h>
namespace android {
@@ -12,16 +15,20 @@
public BnBufferHub {
public:
static status_t start(const std::shared_ptr<BufferHubService>& pdx_service);
- static const char* getServiceName() { return "bufferhubd"; }
// Dump bufferhub related information to given fd (usually stdout)
// usage: adb shell dumpsys bufferhubd
virtual status_t dump(int fd, const Vector<String16>& args) override;
- // Helper function to get the BpReference to this service
- static sp<IBufferHub> getServiceProxy();
+ // Binder IPC functions
+ sp<IBufferClient> createBuffer(uint32_t width, uint32_t height,
+ uint32_t layer_count, uint32_t format,
+ uint64_t usage,
+ uint64_t user_metadata_size) override;
private:
std::shared_ptr<BufferHubService> pdx_service_;
+
+ std::vector<sp<BufferClient>> client_list_;
};
} // namespace dvr
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_node.h b/services/vr/bufferhubd/include/private/dvr/buffer_node.h
index d6c6105..e1e8057 100644
--- a/services/vr/bufferhubd/include/private/dvr/buffer_node.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_node.h
@@ -10,7 +10,7 @@
class BufferNode {
public:
// Creates a BufferNode from existing IonBuffers, i.e. creating from an
- // existing ProducerChannel.
+ // existing ProducerChannel. Allocate a new BufferHubMetadata.
BufferNode(IonBuffer buffer, size_t user_metadata_size);
// Allocates a new BufferNode.
@@ -21,26 +21,56 @@
bool IsValid() const { return buffer_.IsValid() && metadata_.IsValid(); }
size_t user_metadata_size() const { return metadata_.user_metadata_size(); }
- uint64_t active_buffer_bit_mask() const { return active_buffer_bit_mask_; }
- void set_buffer_state_bit(uint64_t buffer_state_bit) {
- active_buffer_bit_mask_ |= buffer_state_bit;
- }
- // Accessor of the IonBuffer.
+ // Accessors of the IonBuffer.
IonBuffer& buffer() { return buffer_; }
const IonBuffer& buffer() const { return buffer_; }
- // Accessor of the metadata.
+ // Accessors of metadata.
const BufferHubMetadata& metadata() const { return metadata_; }
+ // Gets the current value of active_clients_bit_mask in metadata_ with
+ // std::memory_order_acquire, so that all previous releases of
+ // active_clients_bit_mask from all threads will be returned here.
+ uint64_t GetActiveClientsBitMask() const;
+
+ // Find and add a new buffer_state_bit to active_clients_bit_mask in
+ // metadata_.
+ // Return the new buffer_state_bit that is added to active_clients_bit_mask.
+ // Return 0ULL if there are already 32 bp clients of the buffer.
+ uint64_t AddNewActiveClientsBitToMask();
+
+ // Removes the value from active_clients_bit_mask in metadata_ with
+ // std::memory_order_release, so that the change will be visible to any
+ // acquire of active_clients_bit_mask_ in any threads after the succeed of
+ // this operation.
+ void RemoveClientsBitFromMask(const uint64_t& value);
+
private:
+ // Helper method for constructors to initialize atomic metadata header
+ // variables in shared memory.
+ void InitializeMetadata();
+
// Gralloc buffer handles.
IonBuffer buffer_;
+
+ // Metadata in shared memory.
BufferHubMetadata metadata_;
- // All active buffer bits. Valid bits are the lower 63 bits, while the
- // highest bit is reserved for the exclusive writing and should not be set.
- uint64_t active_buffer_bit_mask_ = 0ULL;
+ // The following variables are atomic variables in metadata_ that are visible
+ // to Bn object and Bp objects. Please find more info in
+ // BufferHubDefs::MetadataHeader.
+
+ // buffer_state_ tracks the state of the buffer. Buffer can be in one of these
+ // four states: gained, posted, acquired, released.
+ std::atomic<uint64_t>* buffer_state_ = nullptr;
+
+ // TODO(b/112012161): add comments to fence_state_.
+ std::atomic<uint64_t>* fence_state_ = nullptr;
+
+ // active_clients_bit_mask_ tracks all the bp clients of the buffer. It is the
+ // union of all buffer_state_bit of all bp clients.
+ std::atomic<uint64_t>* active_clients_bit_mask_ = nullptr;
};
} // namespace dvr
diff --git a/services/vr/bufferhubd/tests/Android.bp b/services/vr/bufferhubd/tests/Android.bp
index bf8ea5b..a80691f 100644
--- a/services/vr/bufferhubd/tests/Android.bp
+++ b/services/vr/bufferhubd/tests/Android.bp
@@ -23,4 +23,31 @@
// TODO(b/117568153): Temporarily opt out using libcrt.
no_libcrt: true,
-}
\ No newline at end of file
+}
+
+cc_test {
+ name: "buffer_node-test",
+ srcs: ["buffer_node-test.cpp"],
+ cflags: [
+ "-DLOG_TAG=\"buffer_node-test\"",
+ "-DTRACE=0",
+ "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+ ],
+ header_libs: ["libdvr_headers"],
+ static_libs: [
+ "libbufferhub",
+ "libbufferhubd",
+ "libgmock",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libpdx_default_transport",
+ "libui",
+ "libutils",
+ ],
+ // TODO(b/117568153): Temporarily opt out using libcrt.
+ no_libcrt: true,
+}
+
diff --git a/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
index 587e6db..7fa2226 100644
--- a/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
+++ b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
@@ -1,23 +1,41 @@
+#include <binder/IServiceManager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include <private/dvr/buffer_hub_binder.h>
+#include <private/dvr/IBufferClient.h>
+#include <private/dvr/IBufferHub.h>
+#include <ui/PixelFormat.h>
namespace android {
namespace dvr {
namespace {
-using testing::Ne;
+using testing::NotNull;
+
+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;
class BufferHubBinderServiceTest : public ::testing::Test {
- // Add setup and teardown if necessary
+ protected:
+ void SetUp() override {
+ status_t ret = getService<IBufferHub>(
+ String16(IBufferHub::getServiceName()), &service);
+ ASSERT_EQ(ret, OK);
+ ASSERT_THAT(service, NotNull());
+ }
+
+ sp<IBufferHub> service;
};
-TEST_F(BufferHubBinderServiceTest, TestInitialize) {
- // Create a new service will kill the current one.
- // So just check if Binder service is running
- sp<IBufferHub> service = BufferHubBinderService::getServiceProxy();
- EXPECT_THAT(service, Ne(nullptr));
+TEST_F(BufferHubBinderServiceTest, TestCreateBuffer) {
+ sp<IBufferClient> bufferClient = service->createBuffer(
+ kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
+ ASSERT_THAT(bufferClient, NotNull());
+ EXPECT_TRUE(bufferClient->isValid());
}
} // namespace
diff --git a/services/vr/bufferhubd/tests/buffer_node-test.cpp b/services/vr/bufferhubd/tests/buffer_node-test.cpp
new file mode 100644
index 0000000..c2526fe
--- /dev/null
+++ b/services/vr/bufferhubd/tests/buffer_node-test.cpp
@@ -0,0 +1,89 @@
+#include <errno.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <private/dvr/buffer_node.h>
+
+namespace android {
+namespace dvr {
+
+namespace {
+
+const uint32_t kWidth = 640;
+const uint32_t kHeight = 480;
+const uint32_t kLayerCount = 1;
+const uint32_t kFormat = 1;
+const uint64_t kUsage = 0;
+const size_t kUserMetadataSize = 0;
+
+class BufferNodeTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ buffer_node = new BufferNode(kWidth, kHeight, kLayerCount, kFormat, kUsage,
+ kUserMetadataSize);
+ ASSERT_TRUE(buffer_node->IsValid());
+ }
+
+ void TearDown() override {
+ if (buffer_node != nullptr) {
+ delete buffer_node;
+ }
+ }
+
+ BufferNode* buffer_node = nullptr;
+};
+
+TEST_F(BufferNodeTest, TestCreateBufferNode) {
+ EXPECT_EQ(buffer_node->user_metadata_size(), kUserMetadataSize);
+}
+
+TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_twoNewClients) {
+ uint64_t new_buffer_state_bit_1 = buffer_node->AddNewActiveClientsBitToMask();
+ EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), new_buffer_state_bit_1);
+
+ // Request and add a new buffer_state_bit again.
+ // Active clients bit mask should be the union of the two new
+ // buffer_state_bits.
+ uint64_t new_buffer_state_bit_2 = buffer_node->AddNewActiveClientsBitToMask();
+ EXPECT_EQ(buffer_node->GetActiveClientsBitMask(),
+ new_buffer_state_bit_1 | new_buffer_state_bit_2);
+}
+
+TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_32NewClients) {
+ uint64_t new_buffer_state_bit = 0ULL;
+ uint64_t current_mask = 0ULL;
+ uint64_t expected_mask = 0ULL;
+
+ for (int i = 0; i < 64; ++i) {
+ new_buffer_state_bit = buffer_node->AddNewActiveClientsBitToMask();
+ EXPECT_NE(new_buffer_state_bit, 0);
+ EXPECT_FALSE(new_buffer_state_bit & current_mask);
+ expected_mask = current_mask | new_buffer_state_bit;
+ current_mask = buffer_node->GetActiveClientsBitMask();
+ EXPECT_EQ(current_mask, expected_mask);
+ }
+
+ // Method should fail upon requesting for more than maximum allowable clients.
+ new_buffer_state_bit = buffer_node->AddNewActiveClientsBitToMask();
+ EXPECT_EQ(new_buffer_state_bit, 0ULL);
+ EXPECT_EQ(errno, E2BIG);
+}
+
+TEST_F(BufferNodeTest, TestRemoveActiveClientsBitFromMask) {
+ buffer_node->AddNewActiveClientsBitToMask();
+ uint64_t current_mask = buffer_node->GetActiveClientsBitMask();
+ uint64_t new_buffer_state_bit = buffer_node->AddNewActiveClientsBitToMask();
+ EXPECT_NE(buffer_node->GetActiveClientsBitMask(), current_mask);
+
+ buffer_node->RemoveClientsBitFromMask(new_buffer_state_bit);
+ EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask);
+
+ // Remove the test_mask again to the active client bit mask should not modify
+ // the value of active clients bit mask.
+ buffer_node->RemoveClientsBitFromMask(new_buffer_state_bit);
+ EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask);
+}
+
+} // namespace
+
+} // namespace dvr
+} // namespace android
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 313d095..05d4dfe 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -353,7 +353,7 @@
1,
"",
0,
- VK_API_VERSION_1_0};
+ VK_API_VERSION_1_1};
VkInstanceCreateInfo instance_info = {
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
nullptr,