Merge "Revert "Fix input infos are inconsistent between WMS and InputFlinger (2/2)"" into rvc-dev
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index ec2b922..01b7e63 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2171,7 +2171,7 @@
}
if (ds.options_->do_screenshot) {
- ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.png" : ".png");
+ ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-png.tmp" : ".png");
}
ds.tmp_path_ = ds.GetPath(".tmp");
ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
@@ -2190,7 +2190,7 @@
ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
if (ds.options_->do_zip_file) {
- ds.path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.zip" : ".zip");
+ ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip");
MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
create_parent_dirs(ds.path_.c_str());
ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 70bbc33..1af6edd 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1273,11 +1273,6 @@
Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path,
bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
- // We don't create an image for secondary dex files.
- if (is_secondary_dex) {
- return Dex2oatFileWrapper();
- }
-
const std::string image_path = create_image_filename(out_oat_path);
if (image_path.empty()) {
// Happens when the out_oat_path has an unknown extension.
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index eefbe4f..d773790 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -138,10 +138,10 @@
return 4;
}
- PrepareEnvironment();
+ PrepareEnvironmentVariables();
- if (!PrepareBootImage(/* force */ false)) {
- LOG(ERROR) << "Failed preparing boot image.";
+ if (!EnsureBootImageAndDalvikCache()) {
+ LOG(ERROR) << "Bad boot image.";
return 5;
}
@@ -302,7 +302,7 @@
return parameters_.ReadArguments(argc, const_cast<const char**>(argv));
}
- void PrepareEnvironment() {
+ void PrepareEnvironmentVariables() {
environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_classpath_.c_str()));
environ_.push_back(StringPrintf("ANDROID_DATA=%s", GetOTADataDirectory().c_str()));
environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root_.c_str()));
@@ -312,9 +312,8 @@
}
}
- // Ensure that we have the right boot image. The first time any app is
- // compiled, we'll try to generate it.
- bool PrepareBootImage(bool force) const {
+ // Ensure that we have the right boot image and cache file structures.
+ bool EnsureBootImageAndDalvikCache() const {
if (parameters_.instruction_set == nullptr) {
LOG(ERROR) << "Instruction set missing.";
return false;
@@ -340,34 +339,19 @@
}
}
- // Check whether we have files in /data.
+ // Clear cached artifacts.
+ ClearDirectory(isa_path);
+
+ // Check whether we have a boot image.
// 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;
+ std::string preopted_boot_art_path =
+ StringPrintf("/apex/com.android.art/javalib/%s/boot.art", isa);
+ if (access(preopted_boot_art_path.c_str(), F_OK) != 0) {
+ PLOG(ERROR) << "Bad access() to " << preopted_boot_art_path;
+ return false;
}
- // 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);
- }
-
- return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa);
+ return true;
}
static bool CreatePath(const std::string& path) {
@@ -432,77 +416,6 @@
CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
}
- bool Dex2oatBootImage(const std::string& boot_cp,
- const std::string& art_path,
- const std::string& oat_path,
- const char* isa) const {
- // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
- std::vector<std::string> cmd;
- cmd.push_back(kDex2oatPath);
- cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
- for (const std::string& boot_part : Split(boot_cp, ":")) {
- cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
- }
- cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str()));
-
- int32_t base_offset = ChooseRelocationOffsetDelta(
- art::imagevalues::GetImageMinBaseAddressDelta(),
- art::imagevalues::GetImageMaxBaseAddressDelta());
- cmd.push_back(StringPrintf("--base=0x%x",
- art::imagevalues::GetImageBaseAddress() + base_offset));
-
- cmd.push_back(StringPrintf("--instruction-set=%s", isa));
-
- // These things are pushed by AndroidRuntime, see frameworks/base/core/jni/AndroidRuntime.cpp.
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xms",
- "-Xms",
- true,
- cmd);
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xmx",
- "-Xmx",
- true,
- cmd);
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-filter",
- "--compiler-filter=",
- false,
- cmd);
- cmd.push_back("--profile-file=/system/etc/boot-image.prof");
- // TODO: Compiled-classes.
- const std::string* extra_opts =
- system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags");
- if (extra_opts != nullptr) {
- std::vector<std::string> extra_vals = Split(*extra_opts, " ");
- cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end());
- }
- // TODO: Should we lower this? It's usually set close to max, because
- // normally there's not much else going on at boot.
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-threads",
- "-j",
- false,
- cmd);
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-cpu-set",
- "--cpu-set=",
- false,
- cmd);
- AddCompilerOptionFromSystemProperty(
- StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(),
- "--instruction-set-variant=",
- false,
- cmd);
- AddCompilerOptionFromSystemProperty(
- StringPrintf("dalvik.vm.isa.%s.features", isa).c_str(),
- "--instruction-set-features=",
- false,
- cmd);
-
- std::string error_msg;
- bool result = Exec(cmd, &error_msg);
- if (!result) {
- LOG(ERROR) << "Could not generate boot image: " << error_msg;
- }
- return result;
- }
-
static const char* ParseNull(const char* arg) {
return (strcmp(arg, "!") == 0) ? nullptr : arg;
}
@@ -592,22 +505,6 @@
return 0;
}
- // If the dexopt failed, we may have a stale boot image from a previous OTA run.
- // Then regenerate and retry.
- if (WEXITSTATUS(dexopt_result) ==
- static_cast<int>(::art::dex2oat::ReturnCode::kCreateRuntime)) {
- if (!PrepareBootImage(/* force */ true)) {
- LOG(ERROR) << "Forced boot image creating failed. Original error return was "
- << dexopt_result;
- return dexopt_result;
- }
-
- int dexopt_result_boot_image_retry = Dexopt();
- if (dexopt_result_boot_image_retry == 0) {
- return 0;
- }
- }
-
// If this was a profile-guided run, we may have profile version issues. Try to downgrade,
// if possible.
if ((parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) {
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
new file mode 100644
index 0000000..1acc59d
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
new file mode 100644
index 0000000..4ab9ca4
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
new file mode 100644
index 0000000..d74e673
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
Binary files differ
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 0f4b4d9..3247fa1 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -109,7 +109,7 @@
* It's passed the updated thermal status as parameter, as well as the
* pointer provided by the client that registered a callback.
*/
-typedef int (*AThermal_StatusCallback)(void *data, AThermalStatus status);
+typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
/**
* Acquire an instance of the thermal manager. This must be freed using
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 8ca178c..7ca9031 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -404,6 +404,13 @@
*/
bool hasPendingBatch() const;
+ /* Returns the source of first pending batch if exist.
+ *
+ * Should be called after calling consume() with consumeBatches == false to determine
+ * whether consume() should be called again later on with consumeBatches == true.
+ */
+ int32_t getPendingBatchSource() const;
+
private:
// True if touch resampling is enabled.
const bool mResampleTouch;
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 5e4c98f..4f2709d 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -98,6 +98,15 @@
return PROCESS_STATE_UNKNOWN;
}
+bool ActivityManager::isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
+{
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->isUidActiveOrForeground(uid, callingPackage);
+ }
+ return false;
+}
+
status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
sp<IActivityManager> service = getService();
if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index bc541f4..e6cfeb4 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -158,7 +158,9 @@
filegroup {
name: "libbinder_aidl",
srcs: [
+ "aidl/android/content/pm/IPackageChangeObserver.aidl",
"aidl/android/content/pm/IPackageManagerNative.aidl",
+ "aidl/android/content/pm/PackageChangeEvent.aidl",
"aidl/android/os/IClientCallback.aidl",
"aidl/android/os/IServiceCallback.aidl",
"aidl/android/os/IServiceManager.aidl",
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 1eb5363..9e1249b 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -104,6 +104,18 @@
}
return reply.readInt32();
}
+
+ virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeInt32(uid);
+ data.writeString16(callingPackage);
+ remote()->transact(IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) return false;
+ return reply.readInt32() == 1;
+ }
};
// ------------------------------------------------------------------------------------
diff --git a/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
new file mode 100644
index 0000000..6929a6c
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 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.content.pm;
+
+import android.content.pm.PackageChangeEvent;
+
+/**
+ * This is a non-blocking notification when a package has changed.
+ *
+ * @hide
+ */
+oneway interface IPackageChangeObserver {
+ void onPackageChanged(in PackageChangeEvent event);
+}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 618f88c..dc8d74c 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -17,6 +17,8 @@
package android.content.pm;
+import android.content.pm.IPackageChangeObserver;
+
/**
* Parallel implementation of certain {@link PackageManager} APIs that need to
* be exposed to native code.
@@ -90,4 +92,13 @@
/* Returns the names of all packages. */
@utf8InCpp String[] getAllPackages();
+
+ /** Register an extra package change observer to receive the multi-cast. */
+ void registerPackageChangeObserver(in IPackageChangeObserver observer);
+
+ /**
+ * Unregister an existing package change observer.
+ * This does nothing if this observer was not already registered.
+ */
+ void unregisterPackageChangeObserver(in IPackageChangeObserver observer);
}
diff --git a/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
new file mode 100644
index 0000000..e30e907
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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.content.pm;
+
+/**
+ * This event is designed for notification to native code listener about
+ * any changes on a package including update, deletion and etc.
+ *
+ * @hide
+ */
+parcelable PackageChangeEvent {
+ @utf8InCpp String packageName;
+ long version;
+ long lastUpdateTimeMillis;
+ boolean newInstalled;
+ boolean dataRemoved;
+ boolean isDeleted;
+}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 9108e31..0bb6d28 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -46,25 +46,24 @@
PROCESS_STATE_PERSISTENT = 0,
PROCESS_STATE_PERSISTENT_UI = 1,
PROCESS_STATE_TOP = 2,
- PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
- PROCESS_STATE_BOUND_TOP = 4,
- PROCESS_STATE_FOREGROUND_SERVICE = 5,
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6,
- PROCESS_STATE_IMPORTANT_FOREGROUND = 7,
- PROCESS_STATE_IMPORTANT_BACKGROUND = 8,
- PROCESS_STATE_TRANSIENT_BACKGROUND = 9,
- PROCESS_STATE_BACKUP = 10,
- PROCESS_STATE_SERVICE = 11,
- PROCESS_STATE_RECEIVER = 12,
- PROCESS_STATE_TOP_SLEEPING = 13,
- PROCESS_STATE_HEAVY_WEIGHT = 14,
- PROCESS_STATE_HOME = 15,
- PROCESS_STATE_LAST_ACTIVITY = 16,
- PROCESS_STATE_CACHED_ACTIVITY = 17,
- PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18,
- PROCESS_STATE_CACHED_RECENT = 19,
- PROCESS_STATE_CACHED_EMPTY = 20,
- PROCESS_STATE_NONEXISTENT = 21,
+ PROCESS_STATE_BOUND_TOP = 3,
+ PROCESS_STATE_FOREGROUND_SERVICE = 4,
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5,
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 6,
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 7,
+ PROCESS_STATE_TRANSIENT_BACKGROUND = 8,
+ PROCESS_STATE_BACKUP = 9,
+ PROCESS_STATE_SERVICE = 10,
+ PROCESS_STATE_RECEIVER = 11,
+ PROCESS_STATE_TOP_SLEEPING = 12,
+ PROCESS_STATE_HEAVY_WEIGHT = 13,
+ PROCESS_STATE_HOME = 14,
+ PROCESS_STATE_LAST_ACTIVITY = 15,
+ PROCESS_STATE_CACHED_ACTIVITY = 16,
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17,
+ PROCESS_STATE_CACHED_RECENT = 18,
+ PROCESS_STATE_CACHED_EMPTY = 19,
+ PROCESS_STATE_NONEXISTENT = 20,
};
ActivityManager();
@@ -77,6 +76,7 @@
void unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
+ bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage);
status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e0248f6..1815ecc 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,13 +39,15 @@
virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
+ virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage) = 0;
enum {
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
IS_UID_ACTIVE_TRANSACTION,
- GET_UID_PROCESS_STATE_TRANSACTION
+ GET_UID_PROCESS_STATE_TRANSACTION,
+ IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION,
};
};
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 5b65c95..b2b74dc 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -75,6 +75,7 @@
"android.hardware.automotive.audiocontrol@1.0::IAudioControl",
"android.hardware.automotive.vehicle@2.0::IVehicle",
"android.hardware.automotive.evs@1.0::IEvsCamera",
+ "android.hardware.neuralnetworks@1.0::IDevice",
NULL,
};
diff --git a/libs/gui/tests/AndroidTest.xml b/libs/gui/tests/AndroidTest.xml
index c02e020..5e09fff 100644
--- a/libs/gui/tests/AndroidTest.xml
+++ b/libs/gui/tests/AndroidTest.xml
@@ -18,6 +18,10 @@
<option name="cleanup" value="true" />
<option name="push" value="libgui_test->/data/local/tmp/libgui_test" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+ <option name="screen-always-on" value="on" />
+ </target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 7335b30..ef7cc7d 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -1130,6 +1130,16 @@
return !mBatches.isEmpty();
}
+int32_t InputConsumer::getPendingBatchSource() const {
+ if (mBatches.isEmpty()) {
+ return AINPUT_SOURCE_CLASS_NONE;
+ }
+
+ const Batch& batch = mBatches.itemAt(0);
+ const InputMessage& head = batch.samples.itemAt(0);
+ return head.body.motion.source;
+}
+
ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mBatches.size(); i++) {
const Batch& batch = mBatches.itemAt(i);
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 1075f16..eb6080f 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -55,6 +55,7 @@
"gl/GLShadowTexture.cpp",
"gl/GLShadowVertexGenerator.cpp",
"gl/GLSkiaShadowPort.cpp",
+ "gl/GLVertexBuffer.cpp",
"gl/ImageManager.cpp",
"gl/Program.cpp",
"gl/ProgramCache.cpp",
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 153935b..cb0d5cf 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -32,23 +32,14 @@
namespace gl {
GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine)
- : GLFramebuffer(engine, false /* multiTarget */) {}
-
-GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine, bool multiTarget)
: mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
glGenTextures(1, &mTextureName);
- if (multiTarget) {
- glGenTextures(1, &mSecondaryTextureName);
- }
glGenFramebuffers(1, &mFramebufferName);
}
GLFramebuffer::~GLFramebuffer() {
glDeleteFramebuffers(1, &mFramebufferName);
glDeleteTextures(1, &mTextureName);
- if (mSecondaryTextureName != -1) {
- glDeleteTextures(1, &mSecondaryTextureName);
- }
}
bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
@@ -87,28 +78,12 @@
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
- const bool multiTarget = mSecondaryTextureName != -1;
- if (multiTarget) {
- glBindTexture(GL_TEXTURE_2D, mSecondaryTextureName);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
- }
-
mBufferHeight = height;
mBufferWidth = width;
mEngine.checkErrors("Allocating Fbo texture");
bind();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0);
- if (multiTarget) {
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D,
- mSecondaryTextureName, 0);
- GLenum buffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
- glDrawBuffers(2, buffers);
- }
mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
unbind();
glBindTexture(GL_TEXTURE_2D, 0);
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index 69102d6..b88da3b 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -42,7 +42,6 @@
void allocateBuffers(uint32_t width, uint32_t height);
EGLImageKHR getEGLImage() const { return mEGLImage; }
uint32_t getTextureName() const { return mTextureName; }
- uint32_t getSecondaryTextureName() const { return mSecondaryTextureName; }
uint32_t getFramebufferName() const { return mFramebufferName; }
int32_t getBufferHeight() const { return mBufferHeight; }
int32_t getBufferWidth() const { return mBufferWidth; }
@@ -59,7 +58,6 @@
bool usingFramebufferCache = false;
GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED;
uint32_t mTextureName, mFramebufferName;
- uint32_t mSecondaryTextureName = -1;
int32_t mBufferHeight = 0;
int32_t mBufferWidth = 0;
diff --git a/libs/renderengine/gl/GLVertexBuffer.cpp b/libs/renderengine/gl/GLVertexBuffer.cpp
new file mode 100644
index 0000000..e50c471
--- /dev/null
+++ b/libs/renderengine/gl/GLVertexBuffer.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 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 "GLVertexBuffer.h"
+
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#include <nativebase/nativebase.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLVertexBuffer::GLVertexBuffer() {
+ glGenBuffers(1, &mBufferName);
+}
+
+GLVertexBuffer::~GLVertexBuffer() {
+ glDeleteBuffers(1, &mBufferName);
+}
+
+void GLVertexBuffer::allocateBuffers(const GLfloat data[], const GLuint size) {
+ ATRACE_CALL();
+ bind();
+ glBufferData(GL_ARRAY_BUFFER, size * sizeof(GLfloat), data, GL_STATIC_DRAW);
+ unbind();
+}
+
+void GLVertexBuffer::bind() const {
+ glBindBuffer(GL_ARRAY_BUFFER, mBufferName);
+}
+
+void GLVertexBuffer::unbind() const {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLVertexBuffer.h b/libs/renderengine/gl/GLVertexBuffer.h
new file mode 100644
index 0000000..c0fd0c1
--- /dev/null
+++ b/libs/renderengine/gl/GLVertexBuffer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLVertexBuffer {
+public:
+ explicit GLVertexBuffer();
+ ~GLVertexBuffer();
+
+ void allocateBuffers(const GLfloat data[], const GLuint size);
+ uint32_t getBufferName() const { return mBufferName; }
+ void bind() const;
+ void unbind() const;
+
+private:
+ uint32_t mBufferName;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index e704907..6ba78dc 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -49,6 +49,20 @@
mBUvLoc = mBlurProgram.getAttributeLocation("aUV");
mBTextureLoc = mBlurProgram.getUniformLocation("uTexture");
mBOffsetLoc = mBlurProgram.getUniformLocation("uOffset");
+
+ static constexpr auto size = 2.0f;
+ static constexpr auto translation = 1.0f;
+ const GLfloat vboData[] = {
+ // Vertex data
+ translation-size, -translation-size,
+ translation-size, -translation+size,
+ translation+size, -translation+size,
+ // UV data
+ 0.0f, 0.0f-translation,
+ 0.0f, size-translation,
+ size, size-translation
+ };
+ mMeshBuffer.allocateBuffers(vboData, 12 /* size */);
}
status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
@@ -65,15 +79,23 @@
mPingFbo.allocateBuffers(fboWidth, fboHeight);
mPongFbo.allocateBuffers(fboWidth, fboHeight);
mTexturesAllocated = true;
- }
- if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid blur buffer");
- return mPingFbo.getStatus();
- }
- if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid composition buffer");
- return mCompositionFbo.getStatus();
+ if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid ping buffer");
+ return mPingFbo.getStatus();
+ }
+ if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid pong buffer");
+ return mPongFbo.getStatus();
+ }
+ if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid composition buffer");
+ return mCompositionFbo.getStatus();
+ }
+ if (!mBlurProgram.isValid()) {
+ ALOGE("Invalid shader");
+ return GL_INVALID_OPERATION;
+ }
}
mCompositionFbo.bind();
@@ -82,43 +104,22 @@
}
void BlurFilter::drawMesh(GLuint uv, GLuint position) {
- static constexpr auto size = 2.0f;
- static constexpr auto translation = 1.0f;
- GLfloat positions[] = {
- translation-size, -translation-size,
- translation-size, -translation+size,
- translation+size, -translation+size
- };
- GLfloat texCoords[] = {
- 0.0f, 0.0f-translation,
- 0.0f, size-translation,
- size, size-translation
- };
- // set attributes
glEnableVertexAttribArray(uv);
- glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0, texCoords);
glEnableVertexAttribArray(position);
- glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
- positions);
+ mMeshBuffer.bind();
+ glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE,
+ 2 * sizeof(GLfloat) /* stride */, 0 /* offset */);
+ glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0 /* stride */,
+ (GLvoid*)(6 * sizeof(GLfloat)) /* offset */);
+ mMeshBuffer.unbind();
// draw mesh
glDrawArrays(GL_TRIANGLES, 0 /* first */, 3 /* count */);
- mEngine.checkErrors("Drawing blur mesh");
}
status_t BlurFilter::prepare() {
ATRACE_NAME("BlurFilter::prepare");
-
- if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid FBO");
- return mPongFbo.getStatus();
- }
- if (!mBlurProgram.isValid()) {
- ALOGE("Invalid shader");
- return GL_INVALID_OPERATION;
- }
-
blit(mCompositionFbo, mPingFbo);
// Kawase is an approximation of Gaussian, but it behaves differently from it.
@@ -136,16 +137,15 @@
GLFramebuffer* draw = &mPongFbo;
float stepX = radius / (float)mCompositionFbo.getBufferWidth() / (float)passes;
float stepY = radius / (float)mCompositionFbo.getBufferHeight() / (float)passes;
+ glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
glActiveTexture(GL_TEXTURE0);
glUniform1i(mBTextureLoc, 0);
for (auto i = 0; i < passes; i++) {
ATRACE_NAME("BlurFilter::renderPass");
draw->bind();
- glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
glBindTexture(GL_TEXTURE_2D, read->getTextureName());
glUniform2f(mBOffsetLoc, stepX * i, stepY * i);
- mEngine.checkErrors("Setting uniforms");
drawMesh(mBUvLoc, mBPosLoc);
@@ -189,17 +189,18 @@
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
glUniform1i(mMCompositionTextureLoc, 1);
- mEngine.checkErrors("Setting final pass uniforms");
drawMesh(mMUvLoc, mMPosLoc);
glUseProgram(0);
glActiveTexture(GL_TEXTURE0);
+ mEngine.checkErrors("Drawing blur mesh");
return NO_ERROR;
}
string BlurFilter::getVertexShader() const {
return R"SHADER(#version 310 es
+ precision mediump float;
in vec2 aPosition;
in highp vec2 aUV;
@@ -219,7 +220,7 @@
uniform sampler2D uTexture;
uniform vec2 uOffset;
- highp in vec2 vUV;
+ in highp vec2 vUV;
out vec4 fragColor;
void main() {
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index eb6120b..3eb5c96 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -19,6 +19,7 @@
#include <ui/GraphicTypes.h>
#include "../GLESRenderEngine.h"
#include "../GLFramebuffer.h"
+#include "../GLVertexBuffer.h"
#include "GenericProgram.h"
using namespace std;
@@ -72,6 +73,9 @@
GLFramebuffer* mLastDrawTarget;
bool mTexturesAllocated = false;
+ // VBO containing vertex and uv data of a fullscreen triangle.
+ GLVertexBuffer mMeshBuffer;
+
GenericProgram mMixProgram;
GLuint mMPosLoc;
GLuint mMUvLoc;
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
index d9b713d..c2cda17 100644
--- a/libs/ui/include/ui/Size.h
+++ b/libs/ui/include/ui/Size.h
@@ -19,6 +19,7 @@
#include <algorithm>
#include <cstdint>
#include <limits>
+#include <ostream>
#include <type_traits>
#include <utility>
@@ -113,13 +114,12 @@
std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded &&
std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded,
FromType&&>::type v) {
- static constexpr auto toHighest = std::numeric_limits<remove_cv_reference_t<ToType>>::max();
- static constexpr auto toLowest =
- std::numeric_limits<remove_cv_reference_t<ToType>>::lowest();
- static constexpr auto fromHighest =
- std::numeric_limits<remove_cv_reference_t<FromType>>::max();
- static constexpr auto fromLowest =
- std::numeric_limits<remove_cv_reference_t<FromType>>::lowest();
+ using BareToType = remove_cv_reference_t<ToType>;
+ using BareFromType = remove_cv_reference_t<FromType>;
+ static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
+ static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest();
+ static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
+ static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
// A clamp is needed if the range of FromType is not a subset of the range of ToType
static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest);
@@ -129,10 +129,13 @@
return static_cast<ToType>(v);
}
- // Otherwise we leverage implicit conversion to safely compare values of
- // different types, to ensure we return a value clamped to the range of
- // ToType.
- return v < toLowest ? toLowest : (static_cast<ToType>(v) > toHighest ? toHighest : static_cast<ToType>(v));
+ // Otherwise we need to carefully compare the limits of ToType (casted
+ // for the comparisons to be warning free to FromType) while still
+ // ensuring we return a value clamped to the range of ToType.
+ return v < static_cast<const BareFromType>(toLowest)
+ ? toLowest
+ : (v > static_cast<const BareFromType>(toHighest) ? toHighest
+ : static_cast<ToType>(v));
}
};
@@ -154,5 +157,10 @@
return lhs.height < rhs.height;
}
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Size& size, ::std::ostream* os) {
+ *os << "Size(" << size.width << ", " << size.height << ")";
+}
+
} // namespace ui
} // namespace android
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index ff55bc2..605c5a9 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -111,6 +111,7 @@
cc_test {
name: "Size_test",
+ test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Size_test.cpp"],
cflags: ["-Wall", "-Werror"],
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 69e1ac8..40dc702 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -19,8 +19,18 @@
#include <cmath>
#include <cstdlib>
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wimplicit-int-float-conversion"
+#pragma clang diagnostic error "-Wconversion"
+#endif // __clang__
+
#include <ui/Size.h>
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif // __clang__
+
#include <gtest/gtest.h>
namespace android {
diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING
new file mode 100644
index 0000000..7fcd7de
--- /dev/null
+++ b/libs/ui/tests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "Size_test"
+ }
+ ]
+}
diff --git a/opengl/TEST_MAPPING b/opengl/TEST_MAPPING
new file mode 100644
index 0000000..d391dce
--- /dev/null
+++ b/opengl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsGpuToolsHostTestCases"
+ }
+ ]
+}
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
index 8ff0711..c3da216 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -40,4 +40,8 @@
cflags: [
"-DLOG_TAG=\"AutomotiveDisplayService\""
],
+
+ vintf_fragments: [
+ "manifest_android.frameworks.automotive.display@1.0.xml",
+ ],
}
diff --git a/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
new file mode 100644
index 0000000..464dcac
--- /dev/null
+++ b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+ <hal>
+ <name>android.frameworks.automotive.display</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IAutomotiveDisplayProxyService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index d094532..231d068 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -30,16 +30,11 @@
namespace android {
-GpuStats::GpuStats() {
- AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO,
- GpuStats::pullAtomCallback, nullptr, this);
- AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_APP_INFO,
- GpuStats::pullAtomCallback, nullptr, this);
-}
-
GpuStats::~GpuStats() {
- AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
- AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_APP_INFO);
+ if (mStatsdRegistered) {
+ AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
+ AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_APP_INFO);
+ }
}
static void addLoadingCount(GpuStatsInfo::Driver driver, bool isDriverLoaded,
@@ -97,6 +92,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mLock);
+ registerStatsdCallbacksIfNeeded();
ALOGV("Received:\n"
"\tdriverPackageName[%s]\n"
"\tdriverVersionName[%s]\n"
@@ -150,6 +146,7 @@
const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
std::lock_guard<std::mutex> lock(mLock);
+ registerStatsdCallbacksIfNeeded();
if (!mAppStats.count(appStatsKey)) {
return;
}
@@ -179,6 +176,16 @@
mGlobalStats[0].glesVersion = property_get_int32("ro.opengles.version", 0);
}
+void GpuStats::registerStatsdCallbacksIfNeeded() {
+ if (!mStatsdRegistered) {
+ AStatsManager_setPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO, nullptr,
+ GpuStats::pullAtomCallback, this);
+ AStatsManager_setPullAtomCallback(android::util::GPU_STATS_APP_INFO, nullptr,
+ GpuStats::pullAtomCallback, this);
+ mStatsdRegistered = true;
+ }
+}
+
void GpuStats::dump(const Vector<String16>& args, std::string* result) {
ATRACE_CALL();
diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 8ca4e4e..55f0da1 100644
--- a/services/gpuservice/gpustats/include/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -30,7 +30,6 @@
class GpuStats {
public:
- GpuStats();
~GpuStats();
// Insert new gpu driver stats into global stats and app stats.
@@ -66,12 +65,16 @@
void dumpAppLocked(std::string* result);
// Append cpuVulkanVersion and glesVersion to system driver stats
void interceptSystemDriverStatsLocked();
+ // Registers statsd callbacks if they have not already been registered
+ void registerStatsdCallbacksIfNeeded();
// Below limits the memory usage of GpuStats to be less than 10KB. This is
// the preferred number for statsd while maintaining nice data quality.
static const size_t MAX_NUM_APP_RECORDS = 100;
// GpuStats access should be guarded by mLock.
std::mutex mLock;
+ // True if statsd callbacks have been registered.
+ bool mStatsdRegistered = false;
// Key is driver version code.
std::unordered_map<uint64_t, GpuStatsGlobalInfo> mGlobalStats;
// Key is <app package name>+<driver version code>.
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 4ec4e25..f67c9d0 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -52,7 +52,6 @@
"libstatslog",
"libutils",
"libui",
- "server_configurable_flags",
],
}
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 8ba1f7f..77a0716 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -27,7 +27,6 @@
#if defined(__linux__)
#include <pthread.h>
#endif
-#include <server_configurable_flags/get_flags.h>
#include <unordered_set>
#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
@@ -46,11 +45,6 @@
namespace android {
-// Category (=namespace) name for the input settings that are applied at boot time
-static const char* INPUT_NATIVE_BOOT = "input_native_boot";
-// Feature flag name for the deep press feature
-static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
-
//Max number of elements to store in mEvents.
static constexpr size_t MAX_EVENTS = 5;
@@ -77,20 +71,6 @@
return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
}
-// Check if the "deep touch" feature is on.
-static bool deepPressEnabled() {
- std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
- INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
- std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
- if (flag_value == "1" || flag_value == "true") {
- ALOGI("Deep press feature enabled.");
- return true;
- }
- ALOGI("Deep press feature is not enabled.");
- return false;
-}
-
-
// --- ClassifierEvent ---
ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
@@ -157,12 +137,6 @@
std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
sp<android::hardware::hidl_death_recipient> deathRecipient) {
- if (!deepPressEnabled()) {
- // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
- // When MotionClassifier is null, InputClassifier will forward all events
- // to the next InputListener, unmodified.
- return nullptr;
- }
sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
classifier::V1_0::IInputClassifier::getService();
if (!service) {
@@ -372,14 +346,25 @@
// --- InputClassifier ---
InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
- : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {
- mInitializeMotionClassifierThread = std::thread(
- [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
+ : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
+
+void InputClassifier::setMotionClassifierEnabled(bool enabled) {
+ if (enabled) {
+ ALOGI("Enabling motion classifier");
+ if (mInitializeMotionClassifierThread.joinable()) {
+ mInitializeMotionClassifierThread.join();
+ }
+ mInitializeMotionClassifierThread = std::thread(
+ [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
#if defined(__linux__)
- // Set the thread name for debugging
- pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
- "Create MotionClassifier");
+ // Set the thread name for debugging
+ pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
+ "Create MotionClassifier");
#endif
+ } else {
+ ALOGI("Disabling motion classifier");
+ setMotionClassifier(nullptr);
+ }
}
void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
@@ -429,8 +414,6 @@
void InputClassifier::dump(std::string& dump) {
std::scoped_lock lock(mLock);
dump += "Input Classifier State:\n";
- dump += StringPrintf(INDENT1 "Deep press: %s\n", deepPressEnabled() ? "enabled" : "disabled");
-
dump += INDENT1 "Motion Classifier:\n";
if (mMotionClassifier) {
mMotionClassifier->dump(dump);
@@ -441,7 +424,9 @@
}
InputClassifier::~InputClassifier() {
- mInitializeMotionClassifierThread.join();
+ if (mInitializeMotionClassifierThread.joinable()) {
+ mInitializeMotionClassifierThread.join();
+ }
}
} // namespace android
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 8f58695..03510a6 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -90,6 +90,7 @@
*/
class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
public:
+ virtual void setMotionClassifierEnabled(bool enabled) = 0;
/**
* Dump the state of the input classifier.
* This method may be called on any thread (usually by the input manager).
@@ -234,6 +235,9 @@
~InputClassifier();
+ // Called from InputManager
+ virtual void setMotionClassifierEnabled(bool enabled) override;
+
private:
// Protect access to mMotionClassifier, since it may become null via a hidl callback
std::mutex mLock;
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index c7c61cf..fc771a2 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -132,4 +132,8 @@
mDispatcher->unregisterInputChannel(channel);
}
+void InputManager::setMotionClassifierEnabled(bool enabled) {
+ mClassifier->setMotionClassifierEnabled(enabled);
+}
+
} // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 586097f..0158441 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -100,6 +100,8 @@
virtual void registerInputChannel(const sp<InputChannel>& channel);
virtual void unregisterInputChannel(const sp<InputChannel>& channel);
+ void setMotionClassifierEnabled(bool enabled);
+
private:
sp<InputReaderInterface> mReader;
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index b4e755a..ab74a04 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -135,6 +135,28 @@
ASSERT_EQ(args, outArgs);
}
+TEST_F(InputClassifierTest, SetMotionClassifier_Enabled) {
+ mClassifier->setMotionClassifierEnabled(true);
+}
+
+TEST_F(InputClassifierTest, SetMotionClassifier_Disabled) {
+ mClassifier->setMotionClassifierEnabled(false);
+}
+
+/**
+ * Try to break it by calling setMotionClassifierEnabled multiple times.
+ */
+TEST_F(InputClassifierTest, SetMotionClassifier_Multiple) {
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(false);
+ mClassifier->setMotionClassifierEnabled(false);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+}
+
/**
* A minimal implementation of IInputClassifier.
*/
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 5e04d95..e50a909 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -443,10 +443,7 @@
}
void BufferLayerConsumer::onDisconnect() {
- sp<Layer> l = mLayer.promote();
- if (l.get()) {
- l->onDisconnect();
- }
+ mLayer->onDisconnect();
}
void BufferLayerConsumer::onSidebandStreamChanged() {
@@ -480,10 +477,7 @@
void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
- sp<Layer> l = mLayer.promote();
- if (l.get()) {
- l->addAndGetFrameTimestamps(newTimestamps, outDelta);
- }
+ mLayer->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
void BufferLayerConsumer::abandonLocked() {
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 39ed370..c71a1d9 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -332,7 +332,7 @@
const uint32_t mTexName;
// The layer for this BufferLayerConsumer
- const wp<Layer> mLayer;
+ Layer* mLayer;
wp<ContentsChangedListener> mContentsChangedListener;
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index acaaf4e..2d9f01b 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -35,7 +35,8 @@
return lhs.geometry == rhs.geometry && lhs.alpha == rhs.alpha &&
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
- lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow;
+ lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index e740b13..b738096 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -29,6 +29,7 @@
PowerAdvisor();
~PowerAdvisor() override;
+ MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
MOCK_METHOD0(notifyDisplayUpdateImminent, void());
};
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 06e0cbb..1d8179c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -72,6 +72,10 @@
}
}
+void PowerAdvisor::onBootFinished() {
+ mBootFinished.store(true);
+}
+
void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
if (expected) {
mExpensiveDisplays.insert(displayId);
@@ -97,6 +101,12 @@
}
void PowerAdvisor::notifyDisplayUpdateImminent() {
+ // Only start sending this notification once the system has booted so we don't introduce an
+ // early-boot dependency on Power HAL
+ if (!mBootFinished.load()) {
+ return;
+ }
+
if (mSendUpdateImminent.load()) {
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper == nullptr) {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 957d289..4a90acb 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -35,6 +35,7 @@
public:
virtual ~PowerAdvisor();
+ virtual void onBootFinished() = 0;
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual void notifyDisplayUpdateImminent() = 0;
};
@@ -56,12 +57,14 @@
PowerAdvisor();
~PowerAdvisor() override;
+ void onBootFinished() override;
void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
void notifyDisplayUpdateImminent() override;
private:
HalWrapper* getPowerHal();
+ std::atomic_bool mBootFinished = false;
bool mReconnectPowerHal = false;
std::unordered_set<DisplayId> mExpensiveDisplays;
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 776e984..4e3f85f 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -30,6 +30,7 @@
#include "EventThread.h"
namespace android {
+using base::StringAppendF;
DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
const char* name)
@@ -107,6 +108,12 @@
}
}
+void DispSyncSource::dump(std::string& result) const {
+ std::lock_guard lock(mVsyncMutex);
+ StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled");
+ mDispSync->dump(result);
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 328b8c1..f278712 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -36,6 +36,8 @@
void setCallback(VSyncSource::Callback* callback) override;
void setPhaseOffset(nsecs_t phaseOffset) override;
+ void dump(std::string&) const override;
+
private:
// The following method is the implementation of the DispSync::Callback.
virtual void onDispSyncEvent(nsecs_t when);
@@ -52,7 +54,7 @@
std::mutex mCallbackMutex;
VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
- std::mutex mVsyncMutex;
+ mutable std::mutex mVsyncMutex;
TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
bool mEnabled GUARDED_BY(mVsyncMutex) = false;
};
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index acab5a6..0d6a92e 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -425,7 +425,12 @@
// display is off, keep feeding clients at 60 Hz.
const auto timeout = mState == State::SyntheticVSync ? 16ms : 1000ms;
if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
- ALOGW_IF(mState == State::VSync, "Faking VSYNC due to driver stall");
+ if (mState == State::VSync) {
+ ALOGW("Faking VSYNC due to driver stall for thread %s", mThreadName);
+ std::string debugInfo = "VsyncSource debug info:\n";
+ mVSyncSource->dump(debugInfo);
+ ALOGW("%s", debugInfo.c_str());
+ }
LOG_FATAL_IF(!mVSyncState);
mPendingEvents.push_back(makeVSync(mVSyncState->displayId,
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 466234d..98b1876 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -66,6 +66,8 @@
virtual void setVSyncEnabled(bool enable) = 0;
virtual void setCallback(Callback* callback) = 0;
virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+
+ virtual void dump(std::string& result) const = 0;
};
class EventThreadConnection : public BnDisplayEventConnection {
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index fa46e6f..31da588 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -45,6 +45,7 @@
const char* getName() const override { return "inject"; }
void setVSyncEnabled(bool) override {}
void setPhaseOffset(nsecs_t) override {}
+ void dump(std::string&) const override {}
private:
std::mutex mCallbackMutex;
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index c04447d..43883fb 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -36,6 +36,19 @@
return std::abs(fpsA - fpsB) <= MARGIN;
}
+std::vector<float> getRefreshRatesFromConfigs(
+ const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+ std::vector<float> refreshRates;
+ refreshRates.reserve(allRefreshRates.size());
+
+ for (const auto& [ignored, refreshRate] : allRefreshRates) {
+ refreshRates.emplace_back(refreshRate->fps);
+ }
+
+ return refreshRates;
+}
+
} // namespace
namespace android::scheduler {
@@ -45,14 +58,21 @@
namespace impl {
PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : // Below defines the threshold when an offset is considered to be negative, i.e. targeting
- // for the N+2 vsync instead of N+1. This means that:
- // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
- // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
- mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
- .value_or(std::numeric_limits<nsecs_t>::max())),
- mOffsets(initializeOffsets(refreshRateConfigs)),
- mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {}
+ : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().fps,
+ // Below defines the threshold when an offset is considered to be negative,
+ // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+ // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+ // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+ // vsync.
+ getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+ .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t thresholdForNextVsync)
+ : mThresholdForNextVsync(thresholdForNextVsync),
+ mOffsets(initializeOffsets(refreshRates)),
+ mRefreshRateFps(currentFps) {}
void PhaseOffsets::dump(std::string& result) const {
const auto [early, earlyGl, late] = getCurrentOffsets();
@@ -67,19 +87,24 @@
}
std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
- const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
+ const std::vector<float>& refreshRates) const {
std::unordered_map<float, Offsets> offsets;
- for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
- if (refreshRate->fps > 65.0f) {
- offsets.emplace(refreshRate->fps, getHighFpsOffsets(refreshRate->vsyncPeriod));
- } else {
- offsets.emplace(refreshRate->fps, getDefaultOffsets(refreshRate->vsyncPeriod));
- }
+ for (const auto& refreshRate : refreshRates) {
+ offsets.emplace(refreshRate,
+ getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
}
return offsets;
}
+PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
+ if (fps > 65.0f) {
+ return getHighFpsOffsets(vsyncPeriod);
+ } else {
+ return getDefaultOffsets(vsyncPeriod);
+ }
+}
+
PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000);
const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000);
@@ -159,8 +184,15 @@
[&fps](const std::pair<float, Offsets>& candidateFps) {
return fpsEqualsWithMargin(fps, candidateFps.first);
});
- LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
- return iter->second;
+
+ if (iter != mOffsets.end()) {
+ return iter->second;
+ }
+
+ // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+ // In this case just construct the offset.
+ ALOGW("Can't find offset for %.2f fps", fps);
+ return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
}
static void validateSysprops() {
@@ -231,19 +263,6 @@
};
}
-static std::vector<float> getRefreshRatesFromConfigs(
- const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
- const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
- std::vector<float> refreshRates;
- refreshRates.reserve(allRefreshRates.size());
-
- for (const auto& [ignored, refreshRate] : allRefreshRates) {
- refreshRates.emplace_back(refreshRate->fps);
- }
-
- return refreshRates;
-}
-
std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
const std::vector<float>& refreshRates) const {
std::unordered_map<float, Offsets> offsets;
@@ -288,8 +307,7 @@
return iter->second;
}
- // Unknown refresh rate. This might happen if we get a hotplug event for the default display.
- // This happens only during tests and not during regular device operation.
+ // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
// In this case just construct the offset.
ALOGW("Can't find offset for %.2f fps", fps);
return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index b7d4eae..fa8011d 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -66,11 +66,15 @@
// Returns current offsets in human friendly format.
void dump(std::string& result) const override;
-private:
+protected:
+ // Used for unit tests
+ PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t thresholdForNextVsync);
std::unordered_map<float, Offsets> initializeOffsets(
- const scheduler::RefreshRateConfigs&) const;
- Offsets getDefaultOffsets(nsecs_t vsyncDuration) const;
- Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const;
+ const std::vector<float>& refreshRates) const;
+ Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
+ Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
+ Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
const nsecs_t mThresholdForNextVsync;
const std::unordered_map<float, Offsets> mOffsets;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 920f0ec..cd6075f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -419,20 +419,6 @@
mRefreshRateConfigs.getMaxRefreshRate().fps,
scheduler::LayerHistory::LayerVoteType::Heuristic);
}
-
- // TODO(146935143): Simulate youtube app vote. This should be removed once youtube calls the
- // API to set desired rate
- {
- const auto vote = property_get_int32("experimental.sf.force_youtube_vote", 0);
- if (vote != 0 &&
- layer->getName() ==
- "SurfaceView - "
- "com.google.android.youtube/"
- "com.google.android.apps.youtube.app.WatchWhileActivity#0") {
- layer->setFrameRate(
- Layer::FrameRate(vote, Layer::FrameRateCompatibility::ExactOrMultiple));
- }
- }
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index a6fb3a4..2a2d7c5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -108,6 +108,8 @@
*/
virtual CancelResult cancel(CallbackToken token) = 0;
+ virtual void dump(std::string& result) const = 0;
+
protected:
VSyncDispatch() = default;
VSyncDispatch(VSyncDispatch const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index d0f18ab..460d4a5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -15,6 +15,7 @@
*/
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android-base/stringprintf.h>
#include <utils/Trace.h>
#include <vector>
@@ -23,6 +24,7 @@
#include "VSyncTracker.h"
namespace android::scheduler {
+using base::StringAppendF;
VSyncDispatch::~VSyncDispatch() = default;
VSyncTracker::~VSyncTracker() = default;
@@ -122,6 +124,28 @@
mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
}
+void VSyncDispatchTimerQueueEntry::dump(std::string& result) const {
+ std::lock_guard<std::mutex> lk(mRunningMutex);
+ std::string armedInfo;
+ if (mArmedInfo) {
+ StringAppendF(&armedInfo, "[wake up in %.2fms for vsync %.2fms from now]",
+ (mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
+ (mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
+ }
+
+ StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
+ mRunning ? "(in callback function)" : "", armedInfo.c_str());
+ StringAppendF(&result, "\t\t\tmWorkDuration: %.2fms mEarliestVsync: %.2fms relative to now\n",
+ mWorkDuration / 1e6f, (mEarliestVsync - systemTime()) / 1e6f);
+
+ if (mLastDispatchTime) {
+ StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
+ (systemTime() - *mLastDispatchTime) / 1e6f);
+ } else {
+ StringAppendF(&result, "\t\t\tmLastDispatchTime unknown\n");
+ }
+}
+
VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
VSyncTracker& tracker, nsecs_t timerSlack,
nsecs_t minVsyncDistance)
@@ -296,6 +320,18 @@
return CancelResult::TooLate;
}
+void VSyncDispatchTimerQueue::dump(std::string& result) const {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
+ mMinVsyncDistance / 1e6f);
+ StringAppendF(&result, "\tmIntendedWakeupTime: %.2fms from now\n",
+ (mIntendedWakeupTime - systemTime()) / 1e6f);
+ StringAppendF(&result, "\tCallbacks:\n");
+ for (const auto& [token, entry] : mCallbacks) {
+ entry->dump(result);
+ }
+}
+
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
VSyncDispatch::Callback const& callbackFn,
std::string const& callbackName)
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index fd0a034..390e0c4 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -71,6 +71,8 @@
// Block calling thread while the callback is executing.
void ensureNotRunning();
+ void dump(std::string& result) const;
+
private:
std::string const mName;
VSyncDispatch::Callback const mCallback;
@@ -86,7 +88,7 @@
std::optional<ArmingInfo> mArmedInfo;
std::optional<nsecs_t> mLastDispatchTime;
- std::mutex mRunningMutex;
+ mutable std::mutex mRunningMutex;
std::condition_variable mCv;
bool mRunning GUARDED_BY(mRunningMutex) = false;
};
@@ -112,6 +114,7 @@
void unregisterCallback(CallbackToken token) final;
ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
CancelResult cancel(CallbackToken token) final;
+ void dump(std::string& result) const final;
private:
VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 399da19..a3cb772 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -22,6 +22,7 @@
//#define LOG_NDEBUG 0
#include "VSyncPredictor.h"
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <utils/Log.h>
@@ -31,6 +32,7 @@
#include <sstream>
namespace android::scheduler {
+using base::StringAppendF;
static auto constexpr kMaxPercent = 100u;
@@ -115,10 +117,10 @@
auto it = mRateMap.find(mIdealPeriod);
auto const currentPeriod = std::get<0>(it->second);
// TODO (b/144707443): its important that there's some precision in the mean of the ordinals
- // for the intercept calculation, so scale the ordinals by 10 to continue
+ // for the intercept calculation, so scale the ordinals by 1000 to continue
// fixed point calculation. Explore expanding
// scheduler::utils::calculate_mean to have a fixed point fractional part.
- static constexpr int kScalingFactor = 10;
+ static constexpr int64_t kScalingFactor = 1000;
for (auto i = 0u; i < mTimestamps.size(); i++) {
traceInt64If("VSP-ts", mTimestamps[i]);
@@ -147,7 +149,7 @@
return false;
}
- nsecs_t const anticipatedPeriod = top / bottom * kScalingFactor;
+ nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom;
nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);
auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod;
@@ -263,6 +265,18 @@
clearTimestamps();
}
+void VSyncPredictor::dump(std::string& result) const {
+ std::lock_guard<std::mutex> lk(mMutex);
+ StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f);
+ StringAppendF(&result, "\tRefresh Rate Map:\n");
+ for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
+ StringAppendF(&result,
+ "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
+ idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f,
+ std::get<1>(periodInterceptTuple));
+ }
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index ef1d88a..3ca878d 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -60,6 +60,8 @@
std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
+ void dump(std::string& result) const final;
+
private:
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 8987dcd..892ae62 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -28,6 +28,7 @@
#include "VSyncTracker.h"
namespace android::scheduler {
+using base::StringAppendF;
Clock::~Clock() = default;
nsecs_t SystemClock::now() const {
@@ -73,11 +74,12 @@
public:
CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
nsecs_t period, nsecs_t offset, nsecs_t notBefore)
- : mCallback(cb),
+ : mName(name),
+ mCallback(cb),
mRegistration(dispatch,
std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
std::placeholders::_2),
- std::string(name)),
+ mName),
mPeriod(period),
mOffset(offset),
mLastCallTime(notBefore) {}
@@ -112,6 +114,13 @@
mRegistration.cancel();
}
+ void dump(std::string& result) const {
+ std::lock_guard<std::mutex> lk(mMutex);
+ StringAppendF(&result, "\t%s: mPeriod=%.2f last vsync time %.2fms relative to now (%s)\n",
+ mName.c_str(), mPeriod / 1e6f, (mLastCallTime - systemTime()) / 1e6f,
+ mStopped ? "stopped" : "running");
+ }
+
private:
void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
{
@@ -137,6 +146,7 @@
// Note change in sign between the two defnitions.
nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
+ const std::string mName;
DispSync::Callback* const mCallback;
std::mutex mutable mMutex;
@@ -349,7 +359,35 @@
}
void VSyncReactor::dump(std::string& result) const {
- result += "VsyncReactor in use\n"; // TODO (b/144927823): add more information!
+ std::lock_guard<std::mutex> lk(mMutex);
+ StringAppendF(&result, "VsyncReactor in use\n");
+ StringAppendF(&result, "Has %zu unfired fences\n", mUnfiredFences.size());
+ StringAppendF(&result, "mInternalIgnoreFences=%d mExternalIgnoreFences=%d\n",
+ mInternalIgnoreFences, mExternalIgnoreFences);
+ StringAppendF(&result, "mMoreSamplesNeeded=%d mPeriodConfirmationInProgress=%d\n",
+ mMoreSamplesNeeded, mPeriodConfirmationInProgress);
+ if (mPeriodTransitioningTo) {
+ StringAppendF(&result, "mPeriodTransitioningTo=%" PRId64 "\n", *mPeriodTransitioningTo);
+ } else {
+ StringAppendF(&result, "mPeriodTransitioningTo=nullptr\n");
+ }
+
+ if (mLastHwVsync) {
+ StringAppendF(&result, "Last HW vsync was %.2fms ago\n",
+ (mClock->now() - *mLastHwVsync) / 1e6f);
+ } else {
+ StringAppendF(&result, "No Last HW vsync\n");
+ }
+
+ StringAppendF(&result, "CallbackRepeaters:\n");
+ for (const auto& [callback, repeater] : mCallbacks) {
+ repeater->dump(result);
+ }
+
+ StringAppendF(&result, "VSyncTracker:\n");
+ mTracker->dump(result);
+ StringAppendF(&result, "VSyncDispatch:\n");
+ mDispatch->dump(result);
}
void VSyncReactor::reset() {}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 7d8a8e3..5ee29f8 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -75,7 +75,7 @@
std::unique_ptr<VSyncDispatch> const mDispatch;
size_t const mPendingLimit;
- std::mutex mMutex;
+ mutable std::mutex mMutex;
bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index a25b8a9..05a6fc3 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -66,6 +66,8 @@
/* Inform the tracker that the samples it has are not accurate for prediction. */
virtual void resetModel() = 0;
+ virtual void dump(std::string& result) const = 0;
+
protected:
VSyncTracker(VSyncTracker const&) = delete;
VSyncTracker& operator=(VSyncTracker const&) = delete;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index cfaabfc..2da92b3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -567,6 +567,7 @@
postMessageAsync(new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS {
readPersistentProperties();
+ mPowerAdvisor.onBootFinished();
mBootStage = BootStage::FINISHED;
if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
@@ -1028,7 +1029,7 @@
ATRACE_CALL();
ALOGV("performSetActiveConfig");
if (mCheckPendingFence) {
- if (previousFrameMissed()) {
+ if (previousFramePending()) {
// fence has not signaled yet. wait for the next invalidate
mEventQueue->invalidate();
return true;
@@ -1775,13 +1776,17 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
-bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
- ATRACE_CALL();
+sp<Fence> SurfaceFlinger::previousFrameFence() NO_THREAD_SAFETY_ANALYSIS {
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- const sp<Fence>& fence = mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
+}
+
+bool SurfaceFlinger::previousFramePending(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
+ const sp<Fence>& fence = previousFrameFence();
if (fence == Fence::NO_FENCE) {
return false;
@@ -1794,6 +1799,16 @@
return (fence->getStatus() == Fence::Status::Unsignaled);
}
+nsecs_t SurfaceFlinger::previousFramePresentTime() NO_THREAD_SAFETY_ANALYSIS {
+ const sp<Fence>& fence = previousFrameFence();
+
+ if (fence == Fence::NO_FENCE) {
+ return Fence::SIGNAL_TIME_INVALID;
+ }
+
+ return fence->getSignalTime();
+}
+
void SurfaceFlinger::populateExpectedPresentTime() {
DisplayStatInfo stats;
mScheduler->getDisplayStatInfo(&stats);
@@ -1811,6 +1826,7 @@
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
// seeing this same value.
+ const nsecs_t lastExpectedPresentTime = mExpectedPresentTime.load();
populateExpectedPresentTime();
// When Backpressure propagation is enabled we want to give a small grace period
@@ -1821,12 +1837,32 @@
(mPropagateBackpressureClientComposition || !mHadClientComposition))
? 1
: 0;
- const TracedOrdinal<bool> frameMissed = {"FrameMissed",
- previousFrameMissed(
- graceTimeForPresentFenceMs)};
- const TracedOrdinal<bool> hwcFrameMissed = {"HwcFrameMissed",
+
+ // Pending frames may trigger backpressure propagation.
+ const TracedOrdinal<bool> framePending = {"PrevFramePending",
+ previousFramePending(
+ graceTimeForPresentFenceMs)};
+
+ // Frame missed counts for metrics tracking.
+ // A frame is missed if the prior frame is still pending. If no longer pending,
+ // then we still count the frame as missed if the predicted present time
+ // was further in the past than when the fence actually fired.
+
+ // Add some slop to correct for drift. This should generally be
+ // smaller than a typical frame duration, but should not be so small
+ // that it reports reasonable drift as a missed frame.
+ DisplayStatInfo stats;
+ mScheduler->getDisplayStatInfo(&stats);
+ const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
+ const nsecs_t previousPresentTime = previousFramePresentTime();
+ const TracedOrdinal<bool> frameMissed =
+ {"PrevFrameMissed",
+ framePending ||
+ (previousPresentTime >= 0 &&
+ (lastExpectedPresentTime < previousPresentTime - frameMissedSlop))};
+ const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
mHadDeviceComposition && frameMissed};
- const TracedOrdinal<bool> gpuFrameMissed = {"GpuFrameMissed",
+ const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
mHadClientComposition && frameMissed};
if (frameMissed) {
@@ -1846,7 +1882,7 @@
mGpuFrameMissedCount++;
}
- if (frameMissed && mPropagateBackpressure) {
+ if (framePending && mPropagateBackpressure) {
if ((hwcFrameMissed && !gpuFrameMissed) ||
mPropagateBackpressureClientComposition) {
signalLayerUpdate();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 44e18a7..6ab1fcf 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -844,7 +844,21 @@
bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
- bool previousFrameMissed(int graceTimeMs = 0);
+ // Gets the fence for the previous frame.
+ // Must be called on the main thread.
+ sp<Fence> previousFrameFence();
+
+ // Whether the previous frame has not yet been presented to the display.
+ // If graceTimeMs is positive, this method waits for at most the provided
+ // grace period before reporting if the frame missed.
+ // Must be called on the main thread.
+ bool previousFramePending(int graceTimeMs = 0);
+
+ // Returns the previous time that the frame was presented. If the frame has
+ // not been presented yet, then returns Fence::SIGNAL_TIME_PENDING. If there
+ // is no pending frame, then returns Fence::SIGNAL_TIME_INVALID.
+ // Must be called on the main thread.
+ nsecs_t previousFramePresentTime();
// Populates the expected present time for this frame. For negative offsets, performs a
// correction using the predicted vsync for the next frame instead.
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index 20c8d7a..a9c3332 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -129,7 +129,12 @@
}
status_t SurfaceTracing::writeToFile() {
- mThread.join();
+ std::thread thread;
+ {
+ std::scoped_lock lock(mTraceLock);
+ thread = std::move(mThread);
+ }
+ thread.join();
return mLastErr;
}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 8038eba..4f59bf2 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -188,17 +188,16 @@
TimeStats::~TimeStats() {
std::lock_guard<std::mutex> lock(mMutex);
- mStatsDelegate->unregisterStatsPullAtomCallback(
- android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+ mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+ mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
}
void TimeStats::onBootFinished() {
std::lock_guard<std::mutex> lock(mMutex);
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
+ mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ nullptr, TimeStats::pullAtomCallback, this);
+ mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ nullptr, TimeStats::pullAtomCallback, this);
}
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index ddebeb1..f9bd90b 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -165,15 +165,15 @@
virtual AStatsEvent* addStatsEventToPullData(AStatsEventList* data) {
return AStatsEventList_addStatsEvent(data);
}
- virtual void registerStatsPullAtomCallback(int32_t atom_tag,
- AStatsManager_PullAtomCallback callback,
- AStatsManager_PullAtomMetadata* metadata,
- void* cookie) {
- return AStatsManager_registerPullAtomCallback(atom_tag, callback, metadata, cookie);
+ virtual void setStatsPullAtomCallback(int32_t atom_tag,
+ AStatsManager_PullAtomMetadata* metadata,
+ AStatsManager_PullAtomCallback callback,
+ void* cookie) {
+ return AStatsManager_setPullAtomCallback(atom_tag, metadata, callback, cookie);
}
- virtual void unregisterStatsPullAtomCallback(int32_t atom_tag) {
- return AStatsManager_unregisterPullAtomCallback(atom_tag);
+ virtual void clearStatsPullAtomCallback(int32_t atom_tag) {
+ return AStatsManager_clearPullAtomCallback(atom_tag);
}
virtual void statsEventSetAtomId(AStatsEvent* event, uint32_t atom_id) {
diff --git a/services/surfaceflinger/tests/AndroidTest.xml b/services/surfaceflinger/tests/AndroidTest.xml
index 8315037..000628f 100644
--- a/services/surfaceflinger/tests/AndroidTest.xml
+++ b/services/surfaceflinger/tests/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="cleanup" value="true" />
<option name="push" value="SurfaceFlinger_test->/data/local/tmp/SurfaceFlinger_test" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 65b3e35..ba5c0c2 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -48,6 +48,7 @@
MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
MOCK_METHOD1(pauseVsyncCallback, void(bool));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
};
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
index 910e73b..8d49201 100644
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -42,25 +42,25 @@
appEarlyGlDuration) {}
};
-class PhaseOffsetsTest : public testing::Test {
+class PhaseDurationTest : public testing::Test {
protected:
- PhaseOffsetsTest()
- : mPhaseOffsets(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
- 38'000'000) {}
+ PhaseDurationTest()
+ : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+ 38'000'000) {}
- ~PhaseOffsetsTest() = default;
+ ~PhaseDurationTest() = default;
- TestablePhaseOffsetsAsDurations mPhaseOffsets;
+ TestablePhaseOffsetsAsDurations mPhaseDurations;
};
namespace {
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
- mPhaseOffsets.setRefreshRateFps(60.0f);
- auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
- auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(60.0f);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
+ mPhaseDurations.setRefreshRateFps(60.0f);
+ auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
EXPECT_EQ(currentOffsets, offsets);
EXPECT_EQ(offsets.late.sf, 6'166'667);
@@ -76,10 +76,10 @@
EXPECT_EQ(offsets.earlyGl.app, 15'166'668);
}
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) {
- mPhaseOffsets.setRefreshRateFps(90.0f);
- auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
- auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(90.0f);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
+ mPhaseDurations.setRefreshRateFps(90.0f);
+ auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
EXPECT_EQ(currentOffsets, offsets);
EXPECT_EQ(offsets.late.sf, 611'111);
@@ -95,7 +95,7 @@
EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
}
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) {
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
auto validateOffsets = [](auto& offsets) {
@@ -125,6 +125,54 @@
validateOffsets(offsets);
}
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sf, 57'527'208);
+
+ EXPECT_EQ(offsets.late.app, 37'027'208);
+
+ EXPECT_EQ(offsets.early.sf, 52'027'208);
+
+ EXPECT_EQ(offsets.early.app, 18'527'208);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
+
+ EXPECT_EQ(offsets.earlyGl.app, 16'527'208);
+}
+
+} // namespace
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+ TestablePhaseOffsets() : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 10'000'000) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest() = default;
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsets mPhaseOffsets;
+};
+
+namespace {
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.late.app, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.early.app, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index a7a4d48..4f65aee 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -152,9 +152,9 @@
struct AStatsEvent* addStatsEventToPullData(AStatsEventList*) override {
return mEvent;
}
- void registerStatsPullAtomCallback(int32_t atom_tag,
- AStatsManager_PullAtomCallback callback,
- AStatsManager_PullAtomMetadata*, void* cookie) override {
+ void setStatsPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata*,
+ AStatsManager_PullAtomCallback callback,
+ void* cookie) override {
mAtomTags.push_back(atom_tag);
mCallback = callback;
mCookie = cookie;
@@ -164,7 +164,7 @@
return (*mCallback)(atom_tag, nullptr, cookie);
}
- MOCK_METHOD1(unregisterStatsPullAtomCallback, void(int32_t));
+ MOCK_METHOD1(clearStatsPullAtomCallback, void(int32_t));
MOCK_METHOD2(statsEventSetAtomId, void(AStatsEvent*, uint32_t));
MOCK_METHOD2(statsEventWriteInt32, void(AStatsEvent*, int32_t));
MOCK_METHOD2(statsEventWriteInt64, void(AStatsEvent*, int64_t));
@@ -261,18 +261,18 @@
ASSERT_FALSE(mTimeStats->isEnabled());
}
-TEST_F(TimeStatsTest, registersCallbacksAfterBoot) {
+TEST_F(TimeStatsTest, setsCallbacksAfterBoot) {
mTimeStats->onBootFinished();
EXPECT_THAT(mDelegate->mAtomTags,
UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
android::util::SURFACEFLINGER_STATS_LAYER_INFO));
}
-TEST_F(TimeStatsTest, unregistersCallbacksOnDestruction) {
+TEST_F(TimeStatsTest, clearsCallbacksOnDestruction) {
EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+ clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
mTimeStats.reset();
}
@@ -1039,8 +1039,7 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
- // Now make sure that TimeStats flushes global stats to register the
- // callback.
+ // Now make sure that TimeStats flushes global stats to set the callback.
mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index caac61d..be49ef3 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -51,6 +51,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
+ void dump(std::string&) const final {}
private:
nsecs_t const mPeriod;
@@ -85,6 +86,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
+ void dump(std::string&) const final {}
private:
std::mutex mutable mMutex;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 3ab38e4..3543361 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -47,6 +47,7 @@
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
if (timePoint % mPeriod == 0) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index f834af8..bf2a889 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -204,7 +204,7 @@
};
auto idealPeriod = 2000000;
auto expectedPeriod = 1999892;
- auto expectedIntercept = 175409;
+ auto expectedIntercept = 86342;
tracker.setPeriod(idealPeriod);
for (auto const& timestamp : simulatedVsyncs) {
@@ -335,8 +335,8 @@
158929706370359,
};
auto const idealPeriod = 11111111;
- auto const expectedPeriod = 11113500;
- auto const expectedIntercept = -395335;
+ auto const expectedPeriod = 11113919;
+ auto const expectedIntercept = -1195945;
tracker.setPeriod(idealPeriod);
for (auto const& timestamp : simulatedVsyncs) {
@@ -355,6 +355,32 @@
EXPECT_THAT(prediction, Ge(timePoint));
}
+// See b/151146131
+TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
+ VSyncPredictor tracker{mPeriod, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
+ 840923581635, 840940161584, 840956868096,
+ 840973702473, 840990256277, 841007116851,
+ 841023722530, 841040452167, 841057073002,
+ 841073800920, 841090474360, 841107278632,
+ 841123898634, 841140750875, 841157287127,
+ 841591357014, 840856664232
+
+ };
+ auto const idealPeriod = 16666666;
+ auto const expectedPeriod = 16698426;
+ auto const expectedIntercept = 58055;
+
+ tracker.setPeriod(idealPeriod);
+ for (auto const& timestamp : simulatedVsyncs) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
TEST_F(VSyncPredictorTest, resetsWhenInstructed) {
auto const idealPeriod = 10000;
auto const realPeriod = 10500;
@@ -390,6 +416,22 @@
}
}
+constexpr nsecs_t operator""_years(unsigned long long years) noexcept {
+ using namespace std::chrono_literals;
+ return years * 365 * 24 * 3600 *
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+}
+TEST_F(VSyncPredictorTest, aPhoneThatHasBeenAroundAWhileCanStillComputePeriod) {
+ constexpr nsecs_t timeBase = 100_years;
+
+ for (auto i = 0; i < kHistorySize; i++) {
+ tracker.addVsyncTimestamp(timeBase + i * mPeriod);
+ }
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(mPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, Eq(0));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index ac95938..4f150ef 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -41,6 +41,7 @@
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD1(dump, void(std::string&));
};
class VSyncTrackerWrapper : public VSyncTracker {
@@ -56,6 +57,7 @@
nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
void resetModel() final { mTracker->resetModel(); }
+ void dump(std::string& result) const final { mTracker->dump(result); }
private:
std::shared_ptr<VSyncTracker> const mTracker;
@@ -83,6 +85,7 @@
MOCK_METHOD1(unregisterCallback, void(CallbackToken));
MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
};
class VSyncDispatchWrapper : public VSyncDispatch {
@@ -102,6 +105,8 @@
CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
+ void dump(std::string& result) const final { return mDispatch->dump(result); }
+
private:
std::shared_ptr<VSyncDispatch> const mDispatch;
};
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index fe57c98..e22d0cf 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -29,6 +29,7 @@
PowerAdvisor();
~PowerAdvisor() override;
+ MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
MOCK_METHOD0(notifyDisplayUpdateImminent, void());
};
diff --git a/vulkan/TEST_MAPPING b/vulkan/TEST_MAPPING
new file mode 100644
index 0000000..d391dce
--- /dev/null
+++ b/vulkan/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsGpuToolsHostTestCases"
+ }
+ ]
+}
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index aae72db..5b9affd 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -29,6 +29,8 @@
#include <algorithm>
#include <mutex>
#include <new>
+#include <string>
+#include <unordered_set>
#include <utility>
#include <android-base/strings.h>
@@ -1194,6 +1196,23 @@
return initialized;
}
+template <typename Functor>
+void ForEachLayerFromSettings(Functor functor) {
+ const std::string layersSetting =
+ android::GraphicsEnv::getInstance().getDebugLayers();
+ if (!layersSetting.empty()) {
+ std::vector<std::string> layers =
+ android::base::Split(layersSetting, ":");
+ for (uint32_t i = 0; i < layers.size(); i++) {
+ const Layer* layer = FindLayer(layers[i].c_str());
+ if (!layer) {
+ continue;
+ }
+ functor(layer);
+ }
+ }
+}
+
} // anonymous namespace
VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
@@ -1280,9 +1299,56 @@
return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
}
- // TODO(b/143293104): expose extensions from implicitly enabled layers
- return vulkan::driver::EnumerateInstanceExtensionProperties(
- nullptr, pPropertyCount, pProperties);
+ // If the pLayerName is nullptr, we must advertise all instance extensions
+ // from all implicitly enabled layers and the driver implementation. If
+ // there are duplicates among layers and the driver implementation, always
+ // only preserve the top layer closest to the application regardless of the
+ // spec version.
+ std::vector<VkExtensionProperties> properties;
+ std::unordered_set<std::string> extensionNames;
+
+ // Expose extensions from implicitly enabled layers.
+ ForEachLayerFromSettings([&](const Layer* layer) {
+ uint32_t count = 0;
+ const VkExtensionProperties* props =
+ GetLayerInstanceExtensions(*layer, count);
+ if (count > 0) {
+ for (uint32_t i = 0; i < count; ++i) {
+ if (extensionNames.emplace(props[i].extensionName).second) {
+ properties.push_back(props[i]);
+ }
+ }
+ }
+ });
+
+ // TODO(b/143293104): Parse debug.vulkan.layers properties
+
+ // Expose extensions from driver implementation.
+ {
+ uint32_t count = 0;
+ VkResult result = vulkan::driver::EnumerateInstanceExtensionProperties(
+ nullptr, &count, nullptr);
+ if (result == VK_SUCCESS && count > 0) {
+ std::vector<VkExtensionProperties> props(count);
+ result = vulkan::driver::EnumerateInstanceExtensionProperties(
+ nullptr, &count, props.data());
+ for (auto prop : props) {
+ if (extensionNames.emplace(prop.extensionName).second) {
+ properties.push_back(prop);
+ }
+ }
+ }
+ }
+
+ uint32_t totalCount = properties.size();
+ if (!pProperties || *pPropertyCount > totalCount) {
+ *pPropertyCount = totalCount;
+ }
+ if (pProperties) {
+ std::copy(properties.data(), properties.data() + *pPropertyCount,
+ pProperties);
+ }
+ return *pPropertyCount < totalCount ? VK_INCOMPLETE : VK_SUCCESS;
}
VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
@@ -1334,10 +1400,57 @@
return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
}
- // TODO(b/143293104): expose extensions from implicitly enabled layers
- const InstanceData& data = GetData(physicalDevice);
- return data.dispatch.EnumerateDeviceExtensionProperties(
- physicalDevice, nullptr, pPropertyCount, pProperties);
+ // If the pLayerName is nullptr, we must advertise all device extensions
+ // from all implicitly enabled layers and the driver implementation. If
+ // there are duplicates among layers and the driver implementation, always
+ // only preserve the top layer closest to the application regardless of the
+ // spec version.
+ std::vector<VkExtensionProperties> properties;
+ std::unordered_set<std::string> extensionNames;
+
+ // Expose extensions from implicitly enabled layers.
+ ForEachLayerFromSettings([&](const Layer* layer) {
+ uint32_t count = 0;
+ const VkExtensionProperties* props =
+ GetLayerDeviceExtensions(*layer, count);
+ if (count > 0) {
+ for (uint32_t i = 0; i < count; ++i) {
+ if (extensionNames.emplace(props[i].extensionName).second) {
+ properties.push_back(props[i]);
+ }
+ }
+ }
+ });
+
+ // TODO(b/143293104): Parse debug.vulkan.layers properties
+
+ // Expose extensions from driver implementation.
+ {
+ const InstanceData& data = GetData(physicalDevice);
+ uint32_t count = 0;
+ VkResult result = data.dispatch.EnumerateDeviceExtensionProperties(
+ physicalDevice, nullptr, &count, nullptr);
+ if (result == VK_SUCCESS && count > 0) {
+ std::vector<VkExtensionProperties> props(count);
+ result = data.dispatch.EnumerateDeviceExtensionProperties(
+ physicalDevice, nullptr, &count, props.data());
+ for (auto prop : props) {
+ if (extensionNames.emplace(prop.extensionName).second) {
+ properties.push_back(prop);
+ }
+ }
+ }
+ }
+
+ uint32_t totalCount = properties.size();
+ if (!pProperties || *pPropertyCount > totalCount) {
+ *pPropertyCount = totalCount;
+ }
+ if (pProperties) {
+ std::copy(properties.data(), properties.data() + *pPropertyCount,
+ pProperties);
+ }
+ return *pPropertyCount < totalCount ? VK_INCOMPLETE : VK_SUCCESS;
}
VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {