diff options
90 files changed, 3789 insertions, 1249 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 826a8dbc2c..6b9a0a0e7e 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -4197,14 +4197,14 @@ int Dumpstate::DumpFile(const std::string& title, const std::string& path) { } int read_file_as_long(const char *path, long int *output) { - int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC)); - if (fd < 0) { + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC))); + if (fd.get() < 0) { int err = errno; MYLOGE("Error opening file descriptor for %s: %s\n", path, strerror(err)); return -1; } char buffer[50]; - ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); + ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd.get(), buffer, sizeof(buffer))); if (bytes_read == -1) { MYLOGE("Error reading file %s: %s\n", path, strerror(errno)); return -2; diff --git a/cmds/evemu-record/main.rs b/cmds/evemu-record/main.rs index c30c00fcd2..db3fd77520 100644 --- a/cmds/evemu-record/main.rs +++ b/cmds/evemu-record/main.rs @@ -120,7 +120,7 @@ fn print_device_description( fn print_in_8_byte_chunks( output: &mut impl Write, prefix: &str, - data: &Vec<u8>, + data: &[u8], ) -> Result<(), io::Error> { for (i, byte) in data.iter().enumerate() { if i % 8 == 0 { diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 3f4ee13b95..da232a5935 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -335,12 +335,24 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.vulkan.level-1.prebuilt.xml", + src: "android.hardware.vulkan.level-1.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.vulkan.version-1_0_3.prebuilt.xml", src: "android.hardware.vulkan.version-1_0_3.xml", defaults: ["frameworks_native_data_etc_defaults"], } prebuilt_etc { + name: "android.hardware.vulkan.version-1_3.prebuilt.xml", + src: "android.hardware.vulkan.version-1_3.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.wifi.prebuilt.xml", src: "android.hardware.wifi.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 321737e226..082387e63a 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -346,7 +346,7 @@ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction, */ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, AHardwareBuffer* buffer, - int acquire_fence_fd = -1) __INTRODUCED_IN(29); + int acquire_fence_fd) __INTRODUCED_IN(29); /** * Updates the color for \a surface_control. This will make the background color for the @@ -532,7 +532,7 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transactio * using this API for formats that encode an HDR/SDR ratio as part of generating the buffer. * * @param surface_control The layer whose extended range brightness is being specified - * @param currentBufferRatio The current hdr/sdr ratio of the current buffer as represented as + * @param currentBufferRatio The current HDR/SDR ratio of the current buffer as represented as * peakHdrBrightnessInNits / targetSdrWhitePointInNits. For example if the * buffer was rendered with a target SDR whitepoint of 100nits and a max * display brightness of 200nits, this should be set to 2.0f. @@ -546,7 +546,7 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transactio * * Must be finite && >= 1.0f * - * @param desiredRatio The desired hdr/sdr ratio as represented as peakHdrBrightnessInNits / + * @param desiredRatio The desired HDR/SDR ratio as represented as peakHdrBrightnessInNits / * targetSdrWhitePointInNits. This can be used to communicate the max desired * brightness range. This is similar to the "max luminance" value in other * HDR metadata formats, but represented as a ratio of the target SDR whitepoint @@ -579,13 +579,13 @@ void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* transac float desiredRatio) __INTRODUCED_IN(__ANDROID_API_U__); /** - * Sets the desired hdr headroom for the layer. See: ASurfaceTransaction_setExtendedRangeBrightness, + * Sets the desired HDR headroom for the layer. See: ASurfaceTransaction_setExtendedRangeBrightness, * prefer using this API for formats that conform to HDR standards like HLG or HDR10, that do not * communicate a HDR/SDR ratio as part of generating the buffer. * - * @param surface_control The layer whose desired hdr headroom is being specified + * @param surface_control The layer whose desired HDR headroom is being specified * - * @param desiredHeadroom The desired hdr/sdr ratio as represented as peakHdrBrightnessInNits / + * @param desiredHeadroom The desired HDR/SDR ratio as represented as peakHdrBrightnessInNits / * targetSdrWhitePointInNits. This can be used to communicate the max * desired brightness range of the panel. The system may not be able to, or * may choose not to, deliver the requested range. diff --git a/services/inputflinger/BlockingQueue.h b/include/input/BlockingQueue.h index f848c82c42..f848c82c42 100644 --- a/services/inputflinger/BlockingQueue.h +++ b/include/input/BlockingQueue.h diff --git a/include/input/Input.h b/include/input/Input.h index ddc376809e..19f4ab38e8 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -1192,15 +1192,17 @@ public: */ struct PointerCaptureRequest { public: - inline PointerCaptureRequest() : enable(false), seq(0) {} - inline PointerCaptureRequest(bool enable, uint32_t seq) : enable(enable), seq(seq) {} + inline PointerCaptureRequest() : window(), seq(0) {} + inline PointerCaptureRequest(sp<IBinder> window, uint32_t seq) : window(window), seq(seq) {} inline bool operator==(const PointerCaptureRequest& other) const { - return enable == other.enable && seq == other.seq; + return window == other.window && seq == other.seq; } - explicit inline operator bool() const { return enable; } + inline bool isEnable() const { return window != nullptr; } - // True iff this is a request to enable Pointer Capture. - bool enable; + // The requesting window. + // If the request is to enable the capture, this is the input token of the window that requested + // pointer capture. Otherwise, this is nullptr. + sp<IBinder> window; // The sequence number for the request. uint32_t seq; diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 84ff9d74ef..ca9b08f8ad 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -159,6 +159,11 @@ cc_defaults { "UtilsHost.cpp", ], }, + android: { + lto: { + thin: true, + }, + }, }, aidl: { @@ -219,9 +224,6 @@ cc_defaults { "-performance-move-const-arg", // b/273486801 "portability*", ], - lto: { - thin: true, - }, } cc_library_headers { diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 35cea8132d..1ffdad50e3 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -2980,6 +2980,7 @@ status_t Parcel::restartWrite(size_t desired) uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero); if (!data && desired > mDataCapacity) { + LOG_ALWAYS_FATAL("out of memory"); mError = NO_MEMORY; return NO_MEMORY; } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 8b693390d0..6b8bc01c24 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -135,6 +135,29 @@ rust_bindgen { ], } +cc_library_static { + name: "iinputflinger_aidl_lib_static", + host_supported: true, + srcs: [ + "android/os/IInputFlinger.aidl", + "android/os/InputChannelCore.aidl", + ], + shared_libs: [ + "libbinder", + ], + whole_static_libs: [ + "libgui_window_info_static", + ], + aidl: { + export_aidl_headers: true, + local_include_dirs: ["."], + include_dirs: [ + "frameworks/native/libs/gui", + "frameworks/native/libs/input", + ], + }, +} + // Contains methods to help access C++ code from rust cc_library_static { name: "libinput_from_rust_to_cpp", @@ -154,6 +177,10 @@ cc_library_static { ], generated_sources: ["libinput_cxx_bridge_code"], + lto: { + never: true, + }, + shared_libs: [ "libbase", ], @@ -175,8 +202,6 @@ cc_library { "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", ], srcs: [ - "android/os/IInputFlinger.aidl", - "android/os/InputChannelCore.aidl", "AccelerationCurve.cpp", "Input.cpp", "InputDevice.cpp", @@ -239,7 +264,6 @@ cc_library { static_libs: [ "inputconstants-cpp", - "libgui_window_info_static", "libui-types", "libtflite_static", "libkernelconfigs", @@ -248,10 +272,10 @@ cc_library { whole_static_libs: [ "com.android.input.flags-aconfig-cc", "libinput_rust_ffi", + "iinputflinger_aidl_lib_static", ], export_static_lib_headers: [ - "libgui_window_info_static", "libui-types", ], @@ -285,14 +309,6 @@ cc_library { ], }, }, - - aidl: { - local_include_dirs: ["."], - export_aidl_headers: true, - include_dirs: [ - "frameworks/native/libs/gui", - ], - }, } // Use bootstrap version of stats logging library. diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index ff9d9a94c9..61a964ece9 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -1034,7 +1034,8 @@ std::tuple<int32_t, std::vector<PointerProperties>, std::vector<PointerCoords>> (splitPointerProperties.size() * (historySize + 1))); if (CC_UNLIKELY(splitPointerProperties.size() != splitCount)) { - LOG(FATAL) << "Cannot split MotionEvent: Requested splitting " << splitCount + // TODO(b/329107108): Promote this to a fatal check once bugs in the caller are resolved. + LOG(ERROR) << "Cannot split MotionEvent: Requested splitting " << splitCount << " pointers from the original event, but the original event only contained " << splitPointerProperties.size() << " of those pointers."; } diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index bdec5c33cd..b48b0fb924 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -87,6 +87,7 @@ flag { flag { name: "override_key_behavior_permission_apis" + is_exported: true namespace: "input" description: "enable override key behavior permission APIs" bug: "309018874" @@ -115,6 +116,7 @@ flag { flag { name: "input_device_view_behavior_api" + is_exported: true namespace: "input" description: "Controls the API to provide InputDevice view behavior." bug: "246946631" diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 0485ff6e1e..93af4c2066 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -13,6 +13,7 @@ cc_test { cpp_std: "c++20", host_supported: true, srcs: [ + "BlockingQueue_test.cpp", "IdGenerator_test.cpp", "InputChannel_test.cpp", "InputDevice_test.cpp", diff --git a/services/inputflinger/tests/BlockingQueue_test.cpp b/libs/input/tests/BlockingQueue_test.cpp index 754a5c451e..924b937080 100644 --- a/services/inputflinger/tests/BlockingQueue_test.cpp +++ b/libs/input/tests/BlockingQueue_test.cpp @@ -14,8 +14,7 @@ * limitations under the License. */ -#include "../BlockingQueue.h" - +#include <input/BlockingQueue.h> #include <gtest/gtest.h> #include <thread> @@ -109,7 +108,7 @@ TEST(BlockingQueueTest, Queue_AllowsMultipleThreads) { BlockingQueue<int> queue(capacity); // Fill queue from a different thread - std::thread fillQueue([&queue](){ + std::thread fillQueue([&queue]() { for (size_t i = 0; i < capacity; i++) { ASSERT_TRUE(queue.push(static_cast<int>(i))); } @@ -136,7 +135,7 @@ TEST(BlockingQueueTest, Queue_BlocksWhileWaitingForElements) { std::atomic_bool hasReceivedElement = false; // fill queue from a different thread - std::thread waitUntilHasElements([&queue, &hasReceivedElement](){ + std::thread waitUntilHasElements([&queue, &hasReceivedElement]() { queue.pop(); // This should block until an element has been added hasReceivedElement = true; }); diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index b501d40f26..09d7cb5d3f 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -87,6 +87,7 @@ filegroup { "skia/SkiaRenderEngine.cpp", "skia/SkiaGLRenderEngine.cpp", "skia/SkiaVkRenderEngine.cpp", + "skia/VulkanInterface.cpp", "skia/debug/CaptureTimer.cpp", "skia/debug/CommonPool.cpp", "skia/debug/SkiaCapture.cpp", diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index feb76a1ccd..f2f1b5d0ca 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -33,11 +33,8 @@ #include <sync/sync.h> #include <utils/Trace.h> -#include <cstdint> #include <memory> -#include <sstream> #include <string> -#include <vector> #include <vulkan/vulkan.h> #include "log/log_main.h" @@ -45,619 +42,19 @@ namespace android { namespace renderengine { -struct VulkanFuncs { - PFN_vkCreateSemaphore vkCreateSemaphore = nullptr; - PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr; - PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR = nullptr; - PFN_vkDestroySemaphore vkDestroySemaphore = nullptr; - - PFN_vkDeviceWaitIdle vkDeviceWaitIdle = nullptr; - PFN_vkDestroyDevice vkDestroyDevice = nullptr; - PFN_vkDestroyInstance vkDestroyInstance = nullptr; -}; - -// Ref-Count a semaphore -struct DestroySemaphoreInfo { - VkSemaphore mSemaphore; - // We need to make sure we don't delete the VkSemaphore until it is done being used by both Skia - // (including by the GPU) and inside SkiaVkRenderEngine. So we always start with two refs, one - // owned by Skia and one owned by the SkiaVkRenderEngine. The refs are decremented each time - // delete_semaphore* is called with this object. Skia will call destroy_semaphore* once it is - // done with the semaphore and the GPU has finished work on the semaphore. SkiaVkRenderEngine - // calls delete_semaphore* after sending the semaphore to Skia and exporting it if need be. - int mRefs = 2; - - DestroySemaphoreInfo(VkSemaphore semaphore) : mSemaphore(semaphore) {} -}; - -namespace { -void onVkDeviceFault(void* callbackContext, const std::string& description, - const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, - const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, - const std::vector<std::byte>& vendorBinaryData); -} // anonymous namespace - -struct VulkanInterface { - bool initialized = false; - VkInstance instance; - VkPhysicalDevice physicalDevice; - VkDevice device; - VkQueue queue; - int queueIndex; - uint32_t apiVersion; - GrVkExtensions grExtensions; - VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr; - VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr; - VkPhysicalDeviceProtectedMemoryFeatures* protectedMemoryFeatures = nullptr; - VkPhysicalDeviceFaultFeaturesEXT* deviceFaultFeatures = nullptr; - GrVkGetProc grGetProc; - bool isProtected; - bool isRealtimePriority; - - VulkanFuncs funcs; - - std::vector<std::string> instanceExtensionNames; - std::vector<std::string> deviceExtensionNames; - - GrVkBackendContext getBackendContext() { - GrVkBackendContext backendContext; - backendContext.fInstance = instance; - backendContext.fPhysicalDevice = physicalDevice; - backendContext.fDevice = device; - backendContext.fQueue = queue; - backendContext.fGraphicsQueueIndex = queueIndex; - backendContext.fMaxAPIVersion = apiVersion; - backendContext.fVkExtensions = &grExtensions; - backendContext.fDeviceFeatures2 = physicalDeviceFeatures2; - backendContext.fGetProc = grGetProc; - backendContext.fProtectedContext = isProtected ? GrProtected::kYes : GrProtected::kNo; - backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived - backendContext.fDeviceLostProc = onVkDeviceFault; - return backendContext; - }; - - VkSemaphore createExportableSemaphore() { - VkExportSemaphoreCreateInfo exportInfo; - exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; - exportInfo.pNext = nullptr; - exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; - - VkSemaphoreCreateInfo semaphoreInfo; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - semaphoreInfo.pNext = &exportInfo; - semaphoreInfo.flags = 0; - - VkSemaphore semaphore; - VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore); - if (VK_SUCCESS != err) { - ALOGE("%s: failed to create semaphore. err %d\n", __func__, err); - return VK_NULL_HANDLE; - } - - return semaphore; - } - - // syncFd cannot be <= 0 - VkSemaphore importSemaphoreFromSyncFd(int syncFd) { - VkSemaphoreCreateInfo semaphoreInfo; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - semaphoreInfo.pNext = nullptr; - semaphoreInfo.flags = 0; - - VkSemaphore semaphore; - VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore); - if (VK_SUCCESS != err) { - ALOGE("%s: failed to create import semaphore", __func__); - return VK_NULL_HANDLE; - } - - VkImportSemaphoreFdInfoKHR importInfo; - importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; - importInfo.pNext = nullptr; - importInfo.semaphore = semaphore; - importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT; - importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; - importInfo.fd = syncFd; - - err = funcs.vkImportSemaphoreFdKHR(device, &importInfo); - if (VK_SUCCESS != err) { - funcs.vkDestroySemaphore(device, semaphore, nullptr); - ALOGE("%s: failed to import semaphore", __func__); - return VK_NULL_HANDLE; - } - - return semaphore; - } - - int exportSemaphoreSyncFd(VkSemaphore semaphore) { - int res; - - VkSemaphoreGetFdInfoKHR getFdInfo; - getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; - getFdInfo.pNext = nullptr; - getFdInfo.semaphore = semaphore; - getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; - - VkResult err = funcs.vkGetSemaphoreFdKHR(device, &getFdInfo, &res); - if (VK_SUCCESS != err) { - ALOGE("%s: failed to export semaphore, err: %d", __func__, err); - return -1; - } - return res; - } - - void destroySemaphore(VkSemaphore semaphore) { - funcs.vkDestroySemaphore(device, semaphore, nullptr); - } -}; - -namespace { -void onVkDeviceFault(void* callbackContext, const std::string& description, - const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, - const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, - const std::vector<std::byte>& vendorBinaryData) { - VulkanInterface* interface = static_cast<VulkanInterface*>(callbackContext); - const std::string protectedStr = interface->isProtected ? "protected" : "non-protected"; - // The final crash string should contain as much differentiating info as possible, up to 1024 - // bytes. As this final message is constructed, the same information is also dumped to the logs - // but in a more verbose format. Building the crash string is unsightly, so the clearer logging - // statement is always placed first to give context. - ALOGE("VK_ERROR_DEVICE_LOST (%s context): %s", protectedStr.c_str(), description.c_str()); - std::stringstream crashMsg; - crashMsg << "VK_ERROR_DEVICE_LOST (" << protectedStr; - - if (!addressInfos.empty()) { - ALOGE("%zu VkDeviceFaultAddressInfoEXT:", addressInfos.size()); - crashMsg << ", " << addressInfos.size() << " address info ("; - for (VkDeviceFaultAddressInfoEXT addressInfo : addressInfos) { - ALOGE(" addressType: %d", (int)addressInfo.addressType); - ALOGE(" reportedAddress: %" PRIu64, addressInfo.reportedAddress); - ALOGE(" addressPrecision: %" PRIu64, addressInfo.addressPrecision); - crashMsg << addressInfo.addressType << ":" - << addressInfo.reportedAddress << ":" - << addressInfo.addressPrecision << ", "; - } - crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", " - crashMsg << ")"; - } - - if (!vendorInfos.empty()) { - ALOGE("%zu VkDeviceFaultVendorInfoEXT:", vendorInfos.size()); - crashMsg << ", " << vendorInfos.size() << " vendor info ("; - for (VkDeviceFaultVendorInfoEXT vendorInfo : vendorInfos) { - ALOGE(" description: %s", vendorInfo.description); - ALOGE(" vendorFaultCode: %" PRIu64, vendorInfo.vendorFaultCode); - ALOGE(" vendorFaultData: %" PRIu64, vendorInfo.vendorFaultData); - // Omit descriptions for individual vendor info structs in the crash string, as the - // fault code and fault data fields should be enough for clustering, and the verbosity - // isn't worth it. Additionally, vendors may just set the general description field of - // the overall fault to the description of the first element in this list, and that - // overall description will be placed at the end of the crash string. - crashMsg << vendorInfo.vendorFaultCode << ":" - << vendorInfo.vendorFaultData << ", "; - } - crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", " - crashMsg << ")"; - } - - if (!vendorBinaryData.empty()) { - // TODO: b/322830575 - Log in base64, or dump directly to a file that gets put in bugreports - ALOGE("%zu bytes of vendor-specific binary data (please notify Android's Core Graphics" - " Stack team if you observe this message).", - vendorBinaryData.size()); - crashMsg << ", " << vendorBinaryData.size() << " bytes binary"; - } - - crashMsg << "): " << description; - LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str()); -}; -} // anonymous namespace - -static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) { - if (device != VK_NULL_HANDLE) { - return vkGetDeviceProcAddr(device, proc_name); - } - return vkGetInstanceProcAddr(instance, proc_name); -}; - -#define BAIL(fmt, ...) \ - { \ - ALOGE("%s: " fmt ", bailing", __func__, ##__VA_ARGS__); \ - return interface; \ - } - -#define CHECK_NONNULL(expr) \ - if ((expr) == nullptr) { \ - BAIL("[%s] null", #expr); \ - } - -#define VK_CHECK(expr) \ - if ((expr) != VK_SUCCESS) { \ - BAIL("[%s] failed. err = %d", #expr, expr); \ - return interface; \ - } - -#define VK_GET_PROC(F) \ - PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F); \ - CHECK_NONNULL(vk##F) -#define VK_GET_INST_PROC(instance, F) \ - PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F); \ - CHECK_NONNULL(vk##F) -#define VK_GET_DEV_PROC(device, F) \ - PFN_vk##F vk##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F); \ - CHECK_NONNULL(vk##F) - -VulkanInterface initVulkanInterface(bool protectedContent = false) { - const nsecs_t timeBefore = systemTime(); - VulkanInterface interface; - - VK_GET_PROC(EnumerateInstanceVersion); - uint32_t instanceVersion; - VK_CHECK(vkEnumerateInstanceVersion(&instanceVersion)); - - if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) { - return interface; - } - - const VkApplicationInfo appInfo = { - VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, "surfaceflinger", 0, "android platform", 0, - VK_MAKE_VERSION(1, 1, 0), - }; - - VK_GET_PROC(EnumerateInstanceExtensionProperties); - - uint32_t extensionCount = 0; - VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr)); - std::vector<VkExtensionProperties> instanceExtensions(extensionCount); - VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, - instanceExtensions.data())); - std::vector<const char*> enabledInstanceExtensionNames; - enabledInstanceExtensionNames.reserve(instanceExtensions.size()); - interface.instanceExtensionNames.reserve(instanceExtensions.size()); - for (const auto& instExt : instanceExtensions) { - enabledInstanceExtensionNames.push_back(instExt.extensionName); - interface.instanceExtensionNames.push_back(instExt.extensionName); - } - - const VkInstanceCreateInfo instanceCreateInfo = { - VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - nullptr, - 0, - &appInfo, - 0, - nullptr, - (uint32_t)enabledInstanceExtensionNames.size(), - enabledInstanceExtensionNames.data(), - }; - - VK_GET_PROC(CreateInstance); - VkInstance instance; - VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); - - VK_GET_INST_PROC(instance, DestroyInstance); - interface.funcs.vkDestroyInstance = vkDestroyInstance; - VK_GET_INST_PROC(instance, EnumeratePhysicalDevices); - VK_GET_INST_PROC(instance, EnumerateDeviceExtensionProperties); - VK_GET_INST_PROC(instance, GetPhysicalDeviceProperties2); - VK_GET_INST_PROC(instance, GetPhysicalDeviceExternalSemaphoreProperties); - VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties2); - VK_GET_INST_PROC(instance, GetPhysicalDeviceFeatures2); - VK_GET_INST_PROC(instance, CreateDevice); - - uint32_t physdevCount; - VK_CHECK(vkEnumeratePhysicalDevices(instance, &physdevCount, nullptr)); - if (physdevCount == 0) { - BAIL("Could not find any physical devices"); - } - - physdevCount = 1; - VkPhysicalDevice physicalDevice; - VkResult enumeratePhysDevsErr = - vkEnumeratePhysicalDevices(instance, &physdevCount, &physicalDevice); - if (enumeratePhysDevsErr != VK_SUCCESS && VK_INCOMPLETE != enumeratePhysDevsErr) { - BAIL("vkEnumeratePhysicalDevices failed with non-VK_INCOMPLETE error: %d", - enumeratePhysDevsErr); - } - - VkPhysicalDeviceProperties2 physDevProps = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, - 0, - {}, - }; - VkPhysicalDeviceProtectedMemoryProperties protMemProps = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES, - 0, - {}, - }; - - if (protectedContent) { - physDevProps.pNext = &protMemProps; - } - - vkGetPhysicalDeviceProperties2(physicalDevice, &physDevProps); - if (physDevProps.properties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) { - BAIL("Could not find a Vulkan 1.1+ physical device"); - } - - if (physDevProps.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) { - // TODO: b/326633110 - SkiaVK is not working correctly on swiftshader path. - BAIL("CPU implementations of Vulkan is not supported"); - } - - // Check for syncfd support. Bail if we cannot both import and export them. - VkPhysicalDeviceExternalSemaphoreInfo semInfo = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, - nullptr, - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - }; - VkExternalSemaphoreProperties semProps = { - VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, nullptr, 0, 0, 0, - }; - vkGetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semInfo, &semProps); - - bool sufficientSemaphoreSyncFdSupport = (semProps.exportFromImportedHandleTypes & - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && - (semProps.compatibleHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && - (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) && - (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); - - if (!sufficientSemaphoreSyncFdSupport) { - BAIL("Vulkan device does not support sufficient external semaphore sync fd features. " - "exportFromImportedHandleTypes 0x%x (needed 0x%x) " - "compatibleHandleTypes 0x%x (needed 0x%x) " - "externalSemaphoreFeatures 0x%x (needed 0x%x) ", - semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - semProps.externalSemaphoreFeatures, - VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | - VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); - } else { - ALOGD("Vulkan device supports sufficient external semaphore sync fd features. " - "exportFromImportedHandleTypes 0x%x (needed 0x%x) " - "compatibleHandleTypes 0x%x (needed 0x%x) " - "externalSemaphoreFeatures 0x%x (needed 0x%x) ", - semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, - semProps.externalSemaphoreFeatures, - VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | - VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); - } - - uint32_t queueCount; - vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, nullptr); - if (queueCount == 0) { - BAIL("Could not find queues for physical device"); - } - - std::vector<VkQueueFamilyProperties2> queueProps(queueCount); - std::vector<VkQueueFamilyGlobalPriorityPropertiesEXT> queuePriorityProps(queueCount); - VkQueueGlobalPriorityKHR queuePriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR; - // Even though we don't yet know if the VK_EXT_global_priority extension is available, - // we can safely add the request to the pNext chain, and if the extension is not - // available, it will be ignored. - for (uint32_t i = 0; i < queueCount; ++i) { - queuePriorityProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT; - queuePriorityProps[i].pNext = nullptr; - queueProps[i].pNext = &queuePriorityProps[i]; - } - vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, queueProps.data()); - - int graphicsQueueIndex = -1; - for (uint32_t i = 0; i < queueCount; ++i) { - // Look at potential answers to the VK_EXT_global_priority query. If answers were - // provided, we may adjust the queuePriority. - if (queueProps[i].queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - for (uint32_t j = 0; j < queuePriorityProps[i].priorityCount; j++) { - if (queuePriorityProps[i].priorities[j] > queuePriority) { - queuePriority = queuePriorityProps[i].priorities[j]; - } - } - if (queuePriority == VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR) { - interface.isRealtimePriority = true; - } - graphicsQueueIndex = i; - break; - } - } - - if (graphicsQueueIndex == -1) { - BAIL("Could not find a graphics queue family"); - } - - uint32_t deviceExtensionCount; - VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, - nullptr)); - std::vector<VkExtensionProperties> deviceExtensions(deviceExtensionCount); - VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, - deviceExtensions.data())); - - std::vector<const char*> enabledDeviceExtensionNames; - enabledDeviceExtensionNames.reserve(deviceExtensions.size()); - interface.deviceExtensionNames.reserve(deviceExtensions.size()); - for (const auto& devExt : deviceExtensions) { - enabledDeviceExtensionNames.push_back(devExt.extensionName); - interface.deviceExtensionNames.push_back(devExt.extensionName); - } - - interface.grExtensions.init(sGetProc, instance, physicalDevice, - enabledInstanceExtensionNames.size(), - enabledInstanceExtensionNames.data(), - enabledDeviceExtensionNames.size(), - enabledDeviceExtensionNames.data()); - - if (!interface.grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { - BAIL("Vulkan driver doesn't support external semaphore fd"); - } - - interface.physicalDeviceFeatures2 = new VkPhysicalDeviceFeatures2; - interface.physicalDeviceFeatures2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; - interface.physicalDeviceFeatures2->pNext = nullptr; - - interface.samplerYcbcrConversionFeatures = new VkPhysicalDeviceSamplerYcbcrConversionFeatures; - interface.samplerYcbcrConversionFeatures->sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; - interface.samplerYcbcrConversionFeatures->pNext = nullptr; - - interface.physicalDeviceFeatures2->pNext = interface.samplerYcbcrConversionFeatures; - void** tailPnext = &interface.samplerYcbcrConversionFeatures->pNext; - - if (protectedContent) { - interface.protectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryFeatures; - interface.protectedMemoryFeatures->sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES; - interface.protectedMemoryFeatures->pNext = nullptr; - *tailPnext = interface.protectedMemoryFeatures; - tailPnext = &interface.protectedMemoryFeatures->pNext; - } - - if (interface.grExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) { - interface.deviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT; - interface.deviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT; - interface.deviceFaultFeatures->pNext = nullptr; - *tailPnext = interface.deviceFaultFeatures; - tailPnext = &interface.deviceFaultFeatures->pNext; - } - - vkGetPhysicalDeviceFeatures2(physicalDevice, interface.physicalDeviceFeatures2); - // Looks like this would slow things down and we can't depend on it on all platforms - interface.physicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE; - - if (protectedContent && !interface.protectedMemoryFeatures->protectedMemory) { - BAIL("Protected memory not supported"); - } - - float queuePriorities[1] = {0.0f}; - void* queueNextPtr = nullptr; - - VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo = { - VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT, - nullptr, - // If queue priority is supported, RE should always have realtime priority. - queuePriority, - }; - - if (interface.grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { - queueNextPtr = &queuePriorityCreateInfo; - } - - VkDeviceQueueCreateFlags deviceQueueCreateFlags = - (VkDeviceQueueCreateFlags)(protectedContent ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0); - - const VkDeviceQueueCreateInfo queueInfo = { - VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - queueNextPtr, - deviceQueueCreateFlags, - (uint32_t)graphicsQueueIndex, - 1, - queuePriorities, - }; - - const VkDeviceCreateInfo deviceInfo = { - VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - interface.physicalDeviceFeatures2, - 0, - 1, - &queueInfo, - 0, - nullptr, - (uint32_t)enabledDeviceExtensionNames.size(), - enabledDeviceExtensionNames.data(), - nullptr, - }; - - ALOGD("Trying to create Vk device with protectedContent=%d", protectedContent); - VkDevice device; - VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device)); - ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent); - - VkQueue graphicsQueue; - VK_GET_DEV_PROC(device, GetDeviceQueue2); - const VkDeviceQueueInfo2 deviceQueueInfo2 = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, nullptr, - deviceQueueCreateFlags, - (uint32_t)graphicsQueueIndex, 0}; - vkGetDeviceQueue2(device, &deviceQueueInfo2, &graphicsQueue); - - VK_GET_DEV_PROC(device, DeviceWaitIdle); - VK_GET_DEV_PROC(device, DestroyDevice); - interface.funcs.vkDeviceWaitIdle = vkDeviceWaitIdle; - interface.funcs.vkDestroyDevice = vkDestroyDevice; - - VK_GET_DEV_PROC(device, CreateSemaphore); - VK_GET_DEV_PROC(device, ImportSemaphoreFdKHR); - VK_GET_DEV_PROC(device, GetSemaphoreFdKHR); - VK_GET_DEV_PROC(device, DestroySemaphore); - interface.funcs.vkCreateSemaphore = vkCreateSemaphore; - interface.funcs.vkImportSemaphoreFdKHR = vkImportSemaphoreFdKHR; - interface.funcs.vkGetSemaphoreFdKHR = vkGetSemaphoreFdKHR; - interface.funcs.vkDestroySemaphore = vkDestroySemaphore; - - // At this point, everything's succeeded and we can continue - interface.initialized = true; - interface.instance = instance; - interface.physicalDevice = physicalDevice; - interface.device = device; - interface.queue = graphicsQueue; - interface.queueIndex = graphicsQueueIndex; - interface.apiVersion = physDevProps.properties.apiVersion; - // grExtensions already constructed - // feature pointers already constructed - interface.grGetProc = sGetProc; - interface.isProtected = protectedContent; - // funcs already initialized - - const nsecs_t timeAfter = systemTime(); - const float initTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; - ALOGD("%s: Success init Vulkan interface in %f ms", __func__, initTimeMs); - return interface; -} - -void teardownVulkanInterface(VulkanInterface* interface) { - interface->initialized = false; - - if (interface->device != VK_NULL_HANDLE) { - interface->funcs.vkDeviceWaitIdle(interface->device); - interface->funcs.vkDestroyDevice(interface->device, nullptr); - interface->device = VK_NULL_HANDLE; - } - if (interface->instance != VK_NULL_HANDLE) { - interface->funcs.vkDestroyInstance(interface->instance, nullptr); - interface->instance = VK_NULL_HANDLE; - } - - if (interface->protectedMemoryFeatures) { - delete interface->protectedMemoryFeatures; - } - - if (interface->samplerYcbcrConversionFeatures) { - delete interface->samplerYcbcrConversionFeatures; - } - - if (interface->physicalDeviceFeatures2) { - delete interface->physicalDeviceFeatures2; - } - - if (interface->deviceFaultFeatures) { - delete interface->deviceFaultFeatures; - } - - interface->samplerYcbcrConversionFeatures = nullptr; - interface->physicalDeviceFeatures2 = nullptr; - interface->protectedMemoryFeatures = nullptr; -} - -static VulkanInterface sVulkanInterface; -static VulkanInterface sProtectedContentVulkanInterface; +static skia::VulkanInterface sVulkanInterface; +static skia::VulkanInterface sProtectedContentVulkanInterface; static void sSetupVulkanInterface() { - if (!sVulkanInterface.initialized) { - sVulkanInterface = initVulkanInterface(false /* no protected content */); + if (!sVulkanInterface.isInitialized()) { + sVulkanInterface.init(false /* no protected content */); // We will have to abort if non-protected VkDevice creation fails (then nothing works). - LOG_ALWAYS_FATAL_IF(!sVulkanInterface.initialized, + LOG_ALWAYS_FATAL_IF(!sVulkanInterface.isInitialized(), "Could not initialize Vulkan RenderEngine!"); } - if (!sProtectedContentVulkanInterface.initialized) { - sProtectedContentVulkanInterface = initVulkanInterface(true /* protected content */); - if (!sProtectedContentVulkanInterface.initialized) { + if (!sProtectedContentVulkanInterface.isInitialized()) { + sProtectedContentVulkanInterface.init(true /* protected content */); + if (!sProtectedContentVulkanInterface.isInitialized()) { ALOGE("Could not initialize protected content Vulkan RenderEngine."); } } @@ -668,12 +65,12 @@ bool RenderEngine::canSupport(GraphicsApi graphicsApi) { case GraphicsApi::GL: return true; case GraphicsApi::VK: { - if (!sVulkanInterface.initialized) { - sVulkanInterface = initVulkanInterface(false /* no protected content */); + if (!sVulkanInterface.isInitialized()) { + sVulkanInterface.init(false /* no protected content */); ALOGD("%s: initialized == %s.", __func__, - sVulkanInterface.initialized ? "true" : "false"); + sVulkanInterface.isInitialized() ? "true" : "false"); } - return sVulkanInterface.initialized; + return sVulkanInterface.isInitialized(); } } } @@ -687,7 +84,7 @@ std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( std::unique_ptr<SkiaVkRenderEngine> engine(new SkiaVkRenderEngine(args)); engine->ensureGrContextsCreated(); - if (sVulkanInterface.initialized) { + if (sVulkanInterface.isInitialized()) { ALOGD("SkiaVkRenderEngine::%s: successfully initialized SkiaVkRenderEngine", __func__); return engine; } else { @@ -722,29 +119,17 @@ SkiaRenderEngine::Contexts SkiaVkRenderEngine::createDirectContexts( } bool SkiaVkRenderEngine::supportsProtectedContentImpl() const { - return sProtectedContentVulkanInterface.initialized; + return sProtectedContentVulkanInterface.isInitialized(); } bool SkiaVkRenderEngine::useProtectedContextImpl(GrProtected) { return true; } -static void delete_semaphore(void* semaphore) { - DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(semaphore); - --info->mRefs; - if (!info->mRefs) { - sVulkanInterface.destroySemaphore(info->mSemaphore); - delete info; - } -} - -static void delete_semaphore_protected(void* semaphore) { - DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(semaphore); - --info->mRefs; - if (!info->mRefs) { - sProtectedContentVulkanInterface.destroySemaphore(info->mSemaphore); - delete info; - } +static void unref_semaphore(void* semaphore) { + SkiaVkRenderEngine::DestroySemaphoreInfo* info = + reinterpret_cast<SkiaVkRenderEngine::DestroySemaphoreInfo*>(semaphore); + info->unref(); } static VulkanInterface& getVulkanInterface(bool protectedContext) { @@ -780,10 +165,10 @@ base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) { GrFlushInfo flushInfo; DestroySemaphoreInfo* destroySemaphoreInfo = nullptr; if (semaphore != VK_NULL_HANDLE) { - destroySemaphoreInfo = new DestroySemaphoreInfo(semaphore); + destroySemaphoreInfo = new DestroySemaphoreInfo(vi, semaphore); flushInfo.fNumSemaphores = 1; flushInfo.fSignalSemaphores = &backendSemaphore; - flushInfo.fFinishedProc = isProtected() ? delete_semaphore_protected : delete_semaphore; + flushInfo.fFinishedProc = unref_semaphore; flushInfo.fFinishedContext = destroySemaphoreInfo; } GrSemaphoresSubmitted submitted = grContext->flush(flushInfo); @@ -803,7 +188,7 @@ base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) { int SkiaVkRenderEngine::getContextPriority() { // EGL_CONTEXT_PRIORITY_REALTIME_NV constexpr int kRealtimePriority = 0x3357; - if (getVulkanInterface(isProtected()).isRealtimePriority) { + if (getVulkanInterface(isProtected()).isRealtimePriority()) { return kRealtimePriority; } else { return 0; @@ -812,21 +197,21 @@ int SkiaVkRenderEngine::getContextPriority() { void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { StringAppendF(&result, "\n ------------RE Vulkan----------\n"); - StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.initialized); + StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.isInitialized()); StringAppendF(&result, "\n Vulkan protected device initialized: %d\n", - sProtectedContentVulkanInterface.initialized); + sProtectedContentVulkanInterface.isInitialized()); - if (!sVulkanInterface.initialized) { + if (!sVulkanInterface.isInitialized()) { return; } StringAppendF(&result, "\n Instance extensions:\n"); - for (const auto& name : sVulkanInterface.instanceExtensionNames) { + for (const auto& name : sVulkanInterface.getInstanceExtensionNames()) { StringAppendF(&result, "\n %s\n", name.c_str()); } StringAppendF(&result, "\n Device extensions:\n"); - for (const auto& name : sVulkanInterface.deviceExtensionNames) { + for (const auto& name : sVulkanInterface.getDeviceExtensionNames()) { StringAppendF(&result, "\n %s\n", name.c_str()); } } diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h index 52bc500a5a..ca0dcbf4ae 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.h +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -20,6 +20,7 @@ #include <vk/GrVkBackendContext.h> #include "SkiaRenderEngine.h" +#include "VulkanInterface.h" namespace android { namespace renderengine { @@ -32,6 +33,42 @@ public: int getContextPriority() override; + class DestroySemaphoreInfo { + public: + DestroySemaphoreInfo() = delete; + DestroySemaphoreInfo(const DestroySemaphoreInfo&) = delete; + DestroySemaphoreInfo& operator=(const DestroySemaphoreInfo&) = delete; + DestroySemaphoreInfo& operator=(DestroySemaphoreInfo&&) = delete; + + DestroySemaphoreInfo(VulkanInterface& vulkanInterface, std::vector<VkSemaphore> semaphores) + : mVulkanInterface(vulkanInterface), mSemaphores(std::move(semaphores)) {} + DestroySemaphoreInfo(VulkanInterface& vulkanInterface, VkSemaphore semaphore) + : DestroySemaphoreInfo(vulkanInterface, std::vector<VkSemaphore>(1, semaphore)) {} + + void unref() { + --mRefs; + if (!mRefs) { + for (VkSemaphore semaphore : mSemaphores) { + mVulkanInterface.destroySemaphore(semaphore); + } + delete this; + } + } + + private: + ~DestroySemaphoreInfo() = default; + + VulkanInterface& mVulkanInterface; + std::vector<VkSemaphore> mSemaphores; + // We need to make sure we don't delete the VkSemaphore until it is done being used by both + // Skia (including by the GPU) and inside SkiaVkRenderEngine. So we always start with two + // refs, one owned by Skia and one owned by the SkiaVkRenderEngine. The refs are decremented + // each time unref() is called on this object. Skia will call unref() once it is done with + // the semaphore and the GPU has finished work on the semaphore. SkiaVkRenderEngine calls + // unref() after sending the semaphore to Skia and exporting it if need be. + int mRefs = 2; + }; + protected: // Implementations of abstract SkiaRenderEngine functions specific to // rendering backend diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp new file mode 100644 index 0000000000..453cdc1196 --- /dev/null +++ b/libs/renderengine/skia/VulkanInterface.cpp @@ -0,0 +1,582 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "RenderEngine" + +#include "VulkanInterface.h" + +#include <include/gpu/GpuTypes.h> +#include <log/log_main.h> +#include <utils/Timers.h> + +#include <cinttypes> +#include <sstream> + +namespace android { +namespace renderengine { +namespace skia { + +GrVkBackendContext VulkanInterface::getBackendContext() { + GrVkBackendContext backendContext; + backendContext.fInstance = mInstance; + backendContext.fPhysicalDevice = mPhysicalDevice; + backendContext.fDevice = mDevice; + backendContext.fQueue = mQueue; + backendContext.fGraphicsQueueIndex = mQueueIndex; + backendContext.fMaxAPIVersion = mApiVersion; + backendContext.fVkExtensions = &mGrExtensions; + backendContext.fDeviceFeatures2 = mPhysicalDeviceFeatures2; + backendContext.fGetProc = mGrGetProc; + backendContext.fProtectedContext = mIsProtected ? Protected::kYes : Protected::kNo; + backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived + backendContext.fDeviceLostProc = onVkDeviceFault; + return backendContext; +}; + +VkSemaphore VulkanInterface::createExportableSemaphore() { + VkExportSemaphoreCreateInfo exportInfo; + exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; + exportInfo.pNext = nullptr; + exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + + VkSemaphoreCreateInfo semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreInfo.pNext = &exportInfo; + semaphoreInfo.flags = 0; + + VkSemaphore semaphore; + VkResult err = mFuncs.vkCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to create semaphore. err %d\n", __func__, err); + return VK_NULL_HANDLE; + } + + return semaphore; +} + +// syncFd cannot be <= 0 +VkSemaphore VulkanInterface::importSemaphoreFromSyncFd(int syncFd) { + VkSemaphoreCreateInfo semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreInfo.pNext = nullptr; + semaphoreInfo.flags = 0; + + VkSemaphore semaphore; + VkResult err = mFuncs.vkCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to create import semaphore", __func__); + return VK_NULL_HANDLE; + } + + VkImportSemaphoreFdInfoKHR importInfo; + importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; + importInfo.pNext = nullptr; + importInfo.semaphore = semaphore; + importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT; + importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + importInfo.fd = syncFd; + + err = mFuncs.vkImportSemaphoreFdKHR(mDevice, &importInfo); + if (VK_SUCCESS != err) { + mFuncs.vkDestroySemaphore(mDevice, semaphore, nullptr); + ALOGE("%s: failed to import semaphore", __func__); + return VK_NULL_HANDLE; + } + + return semaphore; +} + +int VulkanInterface::exportSemaphoreSyncFd(VkSemaphore semaphore) { + int res; + + VkSemaphoreGetFdInfoKHR getFdInfo; + getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; + getFdInfo.pNext = nullptr; + getFdInfo.semaphore = semaphore; + getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + VkResult err = mFuncs.vkGetSemaphoreFdKHR(mDevice, &getFdInfo, &res); + if (VK_SUCCESS != err) { + ALOGE("%s: failed to export semaphore, err: %d", __func__, err); + return -1; + } + return res; +} + +void VulkanInterface::destroySemaphore(VkSemaphore semaphore) { + mFuncs.vkDestroySemaphore(mDevice, semaphore, nullptr); +} + +void VulkanInterface::onVkDeviceFault(void* callbackContext, const std::string& description, + const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, + const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, + const std::vector<std::byte>& vendorBinaryData) { + VulkanInterface* interface = static_cast<VulkanInterface*>(callbackContext); + const std::string protectedStr = interface->mIsProtected ? "protected" : "non-protected"; + // The final crash string should contain as much differentiating info as possible, up to 1024 + // bytes. As this final message is constructed, the same information is also dumped to the logs + // but in a more verbose format. Building the crash string is unsightly, so the clearer logging + // statement is always placed first to give context. + ALOGE("VK_ERROR_DEVICE_LOST (%s context): %s", protectedStr.c_str(), description.c_str()); + std::stringstream crashMsg; + crashMsg << "VK_ERROR_DEVICE_LOST (" << protectedStr; + + if (!addressInfos.empty()) { + ALOGE("%zu VkDeviceFaultAddressInfoEXT:", addressInfos.size()); + crashMsg << ", " << addressInfos.size() << " address info ("; + for (VkDeviceFaultAddressInfoEXT addressInfo : addressInfos) { + ALOGE(" addressType: %d", (int)addressInfo.addressType); + ALOGE(" reportedAddress: %" PRIu64, addressInfo.reportedAddress); + ALOGE(" addressPrecision: %" PRIu64, addressInfo.addressPrecision); + crashMsg << addressInfo.addressType << ":" << addressInfo.reportedAddress << ":" + << addressInfo.addressPrecision << ", "; + } + crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", " + crashMsg << ")"; + } + + if (!vendorInfos.empty()) { + ALOGE("%zu VkDeviceFaultVendorInfoEXT:", vendorInfos.size()); + crashMsg << ", " << vendorInfos.size() << " vendor info ("; + for (VkDeviceFaultVendorInfoEXT vendorInfo : vendorInfos) { + ALOGE(" description: %s", vendorInfo.description); + ALOGE(" vendorFaultCode: %" PRIu64, vendorInfo.vendorFaultCode); + ALOGE(" vendorFaultData: %" PRIu64, vendorInfo.vendorFaultData); + // Omit descriptions for individual vendor info structs in the crash string, as the + // fault code and fault data fields should be enough for clustering, and the verbosity + // isn't worth it. Additionally, vendors may just set the general description field of + // the overall fault to the description of the first element in this list, and that + // overall description will be placed at the end of the crash string. + crashMsg << vendorInfo.vendorFaultCode << ":" << vendorInfo.vendorFaultData << ", "; + } + crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", " + crashMsg << ")"; + } + + if (!vendorBinaryData.empty()) { + // TODO: b/322830575 - Log in base64, or dump directly to a file that gets put in bugreports + ALOGE("%zu bytes of vendor-specific binary data (please notify Android's Core Graphics" + " Stack team if you observe this message).", + vendorBinaryData.size()); + crashMsg << ", " << vendorBinaryData.size() << " bytes binary"; + } + + crashMsg << "): " << description; + LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str()); +}; + +static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) { + if (device != VK_NULL_HANDLE) { + return vkGetDeviceProcAddr(device, proc_name); + } + return vkGetInstanceProcAddr(instance, proc_name); +}; + +#define BAIL(fmt, ...) \ + { \ + ALOGE("%s: " fmt ", bailing", __func__, ##__VA_ARGS__); \ + return; \ + } + +#define CHECK_NONNULL(expr) \ + if ((expr) == nullptr) { \ + BAIL("[%s] null", #expr); \ + } + +#define VK_CHECK(expr) \ + if ((expr) != VK_SUCCESS) { \ + BAIL("[%s] failed. err = %d", #expr, expr); \ + return; \ + } + +#define VK_GET_PROC(F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F); \ + CHECK_NONNULL(vk##F) +#define VK_GET_INST_PROC(instance, F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F); \ + CHECK_NONNULL(vk##F) +#define VK_GET_DEV_PROC(device, F) \ + PFN_vk##F vk##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F); \ + CHECK_NONNULL(vk##F) + +void VulkanInterface::init(bool protectedContent) { + if (isInitialized()) { + ALOGW("Called init on already initialized VulkanInterface"); + return; + } + + const nsecs_t timeBefore = systemTime(); + + VK_GET_PROC(EnumerateInstanceVersion); + uint32_t instanceVersion; + VK_CHECK(vkEnumerateInstanceVersion(&instanceVersion)); + + if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) { + return; + } + + const VkApplicationInfo appInfo = { + VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, "surfaceflinger", 0, "android platform", 0, + VK_MAKE_VERSION(1, 1, 0), + }; + + VK_GET_PROC(EnumerateInstanceExtensionProperties); + + uint32_t extensionCount = 0; + VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr)); + std::vector<VkExtensionProperties> instanceExtensions(extensionCount); + VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, + instanceExtensions.data())); + std::vector<const char*> enabledInstanceExtensionNames; + enabledInstanceExtensionNames.reserve(instanceExtensions.size()); + mInstanceExtensionNames.reserve(instanceExtensions.size()); + for (const auto& instExt : instanceExtensions) { + enabledInstanceExtensionNames.push_back(instExt.extensionName); + mInstanceExtensionNames.push_back(instExt.extensionName); + } + + const VkInstanceCreateInfo instanceCreateInfo = { + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + nullptr, + 0, + &appInfo, + 0, + nullptr, + (uint32_t)enabledInstanceExtensionNames.size(), + enabledInstanceExtensionNames.data(), + }; + + VK_GET_PROC(CreateInstance); + VkInstance instance; + VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); + + VK_GET_INST_PROC(instance, DestroyInstance); + mFuncs.vkDestroyInstance = vkDestroyInstance; + VK_GET_INST_PROC(instance, EnumeratePhysicalDevices); + VK_GET_INST_PROC(instance, EnumerateDeviceExtensionProperties); + VK_GET_INST_PROC(instance, GetPhysicalDeviceProperties2); + VK_GET_INST_PROC(instance, GetPhysicalDeviceExternalSemaphoreProperties); + VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties2); + VK_GET_INST_PROC(instance, GetPhysicalDeviceFeatures2); + VK_GET_INST_PROC(instance, CreateDevice); + + uint32_t physdevCount; + VK_CHECK(vkEnumeratePhysicalDevices(instance, &physdevCount, nullptr)); + if (physdevCount == 0) { + BAIL("Could not find any physical devices"); + } + + physdevCount = 1; + VkPhysicalDevice physicalDevice; + VkResult enumeratePhysDevsErr = + vkEnumeratePhysicalDevices(instance, &physdevCount, &physicalDevice); + if (enumeratePhysDevsErr != VK_SUCCESS && VK_INCOMPLETE != enumeratePhysDevsErr) { + BAIL("vkEnumeratePhysicalDevices failed with non-VK_INCOMPLETE error: %d", + enumeratePhysDevsErr); + } + + VkPhysicalDeviceProperties2 physDevProps = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + 0, + {}, + }; + VkPhysicalDeviceProtectedMemoryProperties protMemProps = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES, + 0, + {}, + }; + + if (protectedContent) { + physDevProps.pNext = &protMemProps; + } + + vkGetPhysicalDeviceProperties2(physicalDevice, &physDevProps); + if (physDevProps.properties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) { + BAIL("Could not find a Vulkan 1.1+ physical device"); + } + + if (physDevProps.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) { + // TODO: b/326633110 - SkiaVK is not working correctly on swiftshader path. + BAIL("CPU implementations of Vulkan is not supported"); + } + + // Check for syncfd support. Bail if we cannot both import and export them. + VkPhysicalDeviceExternalSemaphoreInfo semInfo = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, + nullptr, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + }; + VkExternalSemaphoreProperties semProps = { + VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, nullptr, 0, 0, 0, + }; + vkGetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semInfo, &semProps); + + bool sufficientSemaphoreSyncFdSupport = (semProps.exportFromImportedHandleTypes & + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && + (semProps.compatibleHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) && + (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) && + (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + + if (!sufficientSemaphoreSyncFdSupport) { + BAIL("Vulkan device does not support sufficient external semaphore sync fd features. " + "exportFromImportedHandleTypes 0x%x (needed 0x%x) " + "compatibleHandleTypes 0x%x (needed 0x%x) " + "externalSemaphoreFeatures 0x%x (needed 0x%x) ", + semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.externalSemaphoreFeatures, + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + } else { + ALOGD("Vulkan device supports sufficient external semaphore sync fd features. " + "exportFromImportedHandleTypes 0x%x (needed 0x%x) " + "compatibleHandleTypes 0x%x (needed 0x%x) " + "externalSemaphoreFeatures 0x%x (needed 0x%x) ", + semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + semProps.externalSemaphoreFeatures, + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT); + } + + uint32_t queueCount; + vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, nullptr); + if (queueCount == 0) { + BAIL("Could not find queues for physical device"); + } + + std::vector<VkQueueFamilyProperties2> queueProps(queueCount); + std::vector<VkQueueFamilyGlobalPriorityPropertiesEXT> queuePriorityProps(queueCount); + VkQueueGlobalPriorityKHR queuePriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR; + // Even though we don't yet know if the VK_EXT_global_priority extension is available, + // we can safely add the request to the pNext chain, and if the extension is not + // available, it will be ignored. + for (uint32_t i = 0; i < queueCount; ++i) { + queuePriorityProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT; + queuePriorityProps[i].pNext = nullptr; + queueProps[i].pNext = &queuePriorityProps[i]; + } + vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, queueProps.data()); + + int graphicsQueueIndex = -1; + for (uint32_t i = 0; i < queueCount; ++i) { + // Look at potential answers to the VK_EXT_global_priority query. If answers were + // provided, we may adjust the queuePriority. + if (queueProps[i].queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + for (uint32_t j = 0; j < queuePriorityProps[i].priorityCount; j++) { + if (queuePriorityProps[i].priorities[j] > queuePriority) { + queuePriority = queuePriorityProps[i].priorities[j]; + } + } + if (queuePriority == VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR) { + mIsRealtimePriority = true; + } + graphicsQueueIndex = i; + break; + } + } + + if (graphicsQueueIndex == -1) { + BAIL("Could not find a graphics queue family"); + } + + uint32_t deviceExtensionCount; + VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, + nullptr)); + std::vector<VkExtensionProperties> deviceExtensions(deviceExtensionCount); + VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount, + deviceExtensions.data())); + + std::vector<const char*> enabledDeviceExtensionNames; + enabledDeviceExtensionNames.reserve(deviceExtensions.size()); + mDeviceExtensionNames.reserve(deviceExtensions.size()); + for (const auto& devExt : deviceExtensions) { + enabledDeviceExtensionNames.push_back(devExt.extensionName); + mDeviceExtensionNames.push_back(devExt.extensionName); + } + + mGrExtensions.init(sGetProc, instance, physicalDevice, enabledInstanceExtensionNames.size(), + enabledInstanceExtensionNames.data(), enabledDeviceExtensionNames.size(), + enabledDeviceExtensionNames.data()); + + if (!mGrExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { + BAIL("Vulkan driver doesn't support external semaphore fd"); + } + + mPhysicalDeviceFeatures2 = new VkPhysicalDeviceFeatures2; + mPhysicalDeviceFeatures2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + mPhysicalDeviceFeatures2->pNext = nullptr; + + mSamplerYcbcrConversionFeatures = new VkPhysicalDeviceSamplerYcbcrConversionFeatures; + mSamplerYcbcrConversionFeatures->sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; + mSamplerYcbcrConversionFeatures->pNext = nullptr; + + mPhysicalDeviceFeatures2->pNext = mSamplerYcbcrConversionFeatures; + void** tailPnext = &mSamplerYcbcrConversionFeatures->pNext; + + if (protectedContent) { + mProtectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryFeatures; + mProtectedMemoryFeatures->sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES; + mProtectedMemoryFeatures->pNext = nullptr; + *tailPnext = mProtectedMemoryFeatures; + tailPnext = &mProtectedMemoryFeatures->pNext; + } + + if (mGrExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) { + mDeviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT; + mDeviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT; + mDeviceFaultFeatures->pNext = nullptr; + *tailPnext = mDeviceFaultFeatures; + tailPnext = &mDeviceFaultFeatures->pNext; + } + + vkGetPhysicalDeviceFeatures2(physicalDevice, mPhysicalDeviceFeatures2); + // Looks like this would slow things down and we can't depend on it on all platforms + mPhysicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE; + + if (protectedContent && !mProtectedMemoryFeatures->protectedMemory) { + BAIL("Protected memory not supported"); + } + + float queuePriorities[1] = {0.0f}; + void* queueNextPtr = nullptr; + + VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo = { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT, + nullptr, + // If queue priority is supported, RE should always have realtime priority. + queuePriority, + }; + + if (mGrExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { + queueNextPtr = &queuePriorityCreateInfo; + } + + VkDeviceQueueCreateFlags deviceQueueCreateFlags = + (VkDeviceQueueCreateFlags)(protectedContent ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0); + + const VkDeviceQueueCreateInfo queueInfo = { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + queueNextPtr, + deviceQueueCreateFlags, + (uint32_t)graphicsQueueIndex, + 1, + queuePriorities, + }; + + const VkDeviceCreateInfo deviceInfo = { + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + mPhysicalDeviceFeatures2, + 0, + 1, + &queueInfo, + 0, + nullptr, + (uint32_t)enabledDeviceExtensionNames.size(), + enabledDeviceExtensionNames.data(), + nullptr, + }; + + ALOGD("Trying to create Vk device with protectedContent=%d", protectedContent); + VkDevice device; + VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device)); + ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent); + + VkQueue graphicsQueue; + VK_GET_DEV_PROC(device, GetDeviceQueue2); + const VkDeviceQueueInfo2 deviceQueueInfo2 = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, nullptr, + deviceQueueCreateFlags, + (uint32_t)graphicsQueueIndex, 0}; + vkGetDeviceQueue2(device, &deviceQueueInfo2, &graphicsQueue); + + VK_GET_DEV_PROC(device, DeviceWaitIdle); + VK_GET_DEV_PROC(device, DestroyDevice); + mFuncs.vkDeviceWaitIdle = vkDeviceWaitIdle; + mFuncs.vkDestroyDevice = vkDestroyDevice; + + VK_GET_DEV_PROC(device, CreateSemaphore); + VK_GET_DEV_PROC(device, ImportSemaphoreFdKHR); + VK_GET_DEV_PROC(device, GetSemaphoreFdKHR); + VK_GET_DEV_PROC(device, DestroySemaphore); + mFuncs.vkCreateSemaphore = vkCreateSemaphore; + mFuncs.vkImportSemaphoreFdKHR = vkImportSemaphoreFdKHR; + mFuncs.vkGetSemaphoreFdKHR = vkGetSemaphoreFdKHR; + mFuncs.vkDestroySemaphore = vkDestroySemaphore; + + // At this point, everything's succeeded and we can continue + mInitialized = true; + mInstance = instance; + mPhysicalDevice = physicalDevice; + mDevice = device; + mQueue = graphicsQueue; + mQueueIndex = graphicsQueueIndex; + mApiVersion = physDevProps.properties.apiVersion; + // grExtensions already constructed + // feature pointers already constructed + mGrGetProc = sGetProc; + mIsProtected = protectedContent; + // mIsRealtimePriority already initialized by constructor + // funcs already initialized + + const nsecs_t timeAfter = systemTime(); + const float initTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; + ALOGD("%s: Success init Vulkan interface in %f ms", __func__, initTimeMs); +} + +// TODO: b/293371537 - Iterate on this. +// Currently unused, but copied over from its original location for potential future use. This +// should likely be improved to walk the pNext chain of mPhysicalDeviceFeatures2 and free everything +// like HWUI's VulkanManager. Also, not all fields are being reset. +void VulkanInterface::teardown() { + mInitialized = false; + + if (mDevice != VK_NULL_HANDLE) { + mFuncs.vkDeviceWaitIdle(mDevice); + mFuncs.vkDestroyDevice(mDevice, nullptr); + mDevice = VK_NULL_HANDLE; + } + if (mInstance != VK_NULL_HANDLE) { + mFuncs.vkDestroyInstance(mInstance, nullptr); + mInstance = VK_NULL_HANDLE; + } + + if (mProtectedMemoryFeatures) { + delete mProtectedMemoryFeatures; + } + + if (mSamplerYcbcrConversionFeatures) { + delete mSamplerYcbcrConversionFeatures; + } + + if (mPhysicalDeviceFeatures2) { + delete mPhysicalDeviceFeatures2; + } + + if (mDeviceFaultFeatures) { + delete mDeviceFaultFeatures; + } + + mSamplerYcbcrConversionFeatures = nullptr; + mPhysicalDeviceFeatures2 = nullptr; + mProtectedMemoryFeatures = nullptr; + mDeviceFaultFeatures = nullptr; +} + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/VulkanInterface.h b/libs/renderengine/skia/VulkanInterface.h new file mode 100644 index 0000000000..c3936d9869 --- /dev/null +++ b/libs/renderengine/skia/VulkanInterface.h @@ -0,0 +1,95 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <include/gpu/vk/GrVkBackendContext.h> +#include <include/gpu/vk/GrVkExtensions.h> + +#include <vulkan/vulkan.h> + +using namespace skgpu; + +namespace android { +namespace renderengine { +namespace skia { + +class VulkanInterface { +public: + // Create an uninitialized interface. Initialize with `init`. + VulkanInterface() = default; + ~VulkanInterface() = default; + VulkanInterface(const VulkanInterface&) = delete; + VulkanInterface& operator=(const VulkanInterface&) = delete; + VulkanInterface& operator=(VulkanInterface&&) = delete; + + void init(bool protectedContent = false); + void teardown(); + + // TODO: b/293371537 - Graphite variant (external/skia/include/gpu/vk/VulkanBackendContext.h) + GrVkBackendContext getBackendContext(); + VkSemaphore createExportableSemaphore(); + VkSemaphore importSemaphoreFromSyncFd(int syncFd); + int exportSemaphoreSyncFd(VkSemaphore semaphore); + void destroySemaphore(VkSemaphore semaphore); + + bool isInitialized() const { return mInitialized; } + bool isRealtimePriority() const { return mIsRealtimePriority; } + const std::vector<std::string>& getInstanceExtensionNames() { return mInstanceExtensionNames; } + const std::vector<std::string>& getDeviceExtensionNames() { return mDeviceExtensionNames; } + +private: + struct VulkanFuncs { + PFN_vkCreateSemaphore vkCreateSemaphore = nullptr; + PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr; + PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR = nullptr; + PFN_vkDestroySemaphore vkDestroySemaphore = nullptr; + + PFN_vkDeviceWaitIdle vkDeviceWaitIdle = nullptr; + PFN_vkDestroyDevice vkDestroyDevice = nullptr; + PFN_vkDestroyInstance vkDestroyInstance = nullptr; + }; + + static void onVkDeviceFault(void* callbackContext, const std::string& description, + const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, + const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, + const std::vector<std::byte>& vendorBinaryData); + + bool mInitialized = false; + VkInstance mInstance = VK_NULL_HANDLE; + VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; + VkDevice mDevice = VK_NULL_HANDLE; + VkQueue mQueue = VK_NULL_HANDLE; + int mQueueIndex = 0; + uint32_t mApiVersion = 0; + GrVkExtensions mGrExtensions; + VkPhysicalDeviceFeatures2* mPhysicalDeviceFeatures2 = nullptr; + VkPhysicalDeviceSamplerYcbcrConversionFeatures* mSamplerYcbcrConversionFeatures = nullptr; + VkPhysicalDeviceProtectedMemoryFeatures* mProtectedMemoryFeatures = nullptr; + VkPhysicalDeviceFaultFeaturesEXT* mDeviceFaultFeatures = nullptr; + GrVkGetProc mGrGetProc = nullptr; + bool mIsProtected = false; + bool mIsRealtimePriority = false; + + VulkanFuncs mFuncs; + + std::vector<std::string> mInstanceExtensionNames; + std::vector<std::string> mDeviceExtensionNames; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/tracing_perfetto/.clang-format b/libs/tracing_perfetto/.clang-format new file mode 100644 index 0000000000..f3974548f6 --- /dev/null +++ b/libs/tracing_perfetto/.clang-format @@ -0,0 +1,12 @@ +BasedOnStyle: Google +AllowShortBlocksOnASingleLine: false +AllowShortFunctionsOnASingleLine: false + +ColumnLimit: 80 +ContinuationIndentWidth: 4 +CommentPragmas: NOLINT:.* +DerivePointerAlignment: false +IndentWidth: 2 +PointerAlignment: Left +UseTab: Never +PenaltyExcessCharacter: 32
\ No newline at end of file diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp new file mode 100644 index 0000000000..3a4c869e46 --- /dev/null +++ b/libs/tracing_perfetto/Android.bp @@ -0,0 +1,49 @@ +// Copyright 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_library_shared { + name: "libtracing_perfetto", + export_include_dirs: [ + "include", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wno-enum-compare", + "-Wno-unused-function", + ], + + srcs: [ + "tracing_perfetto.cpp", + "tracing_perfetto_internal.cpp", + ], + + shared_libs: [ + "libcutils", + "libperfetto_c", + "android.os.flags-aconfig-cc-host", + ], + + host_supported: true, +} diff --git a/libs/tracing_perfetto/OWNERS b/libs/tracing_perfetto/OWNERS new file mode 100644 index 0000000000..e2d4b4636a --- /dev/null +++ b/libs/tracing_perfetto/OWNERS @@ -0,0 +1,2 @@ +zezeozue@google.com +biswarupp@google.com
\ No newline at end of file diff --git a/libs/tracing_perfetto/include/trace_categories.h b/libs/tracing_perfetto/include/trace_categories.h new file mode 100644 index 0000000000..6d4168b772 --- /dev/null +++ b/libs/tracing_perfetto/include/trace_categories.h @@ -0,0 +1,65 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TRACE_CATEGORIES_H +#define TRACE_CATEGORIES_H + +/** + * Keep these in sync with frameworks/base/core/java/android/os/Trace.java. + */ +#define TRACE_CATEGORY_ALWAYS (1 << 0) +#define TRACE_CATEGORY_GRAPHICS (1 << 1) +#define TRACE_CATEGORY_INPUT (1 << 2) +#define TRACE_CATEGORY_VIEW (1 << 3) +#define TRACE_CATEGORY_WEBVIEW (1 << 4) +#define TRACE_CATEGORY_WINDOW_MANAGER (1 << 5) +#define TRACE_CATEGORY_ACTIVITY_MANAGER (1 << 6) +#define TRACE_CATEGORY_SYNC_MANAGER (1 << 7) +#define TRACE_CATEGORY_AUDIO (1 << 8) +#define TRACE_CATEGORY_VIDEO (1 << 9) +#define TRACE_CATEGORY_CAMERA (1 << 10) +#define TRACE_CATEGORY_HAL (1 << 11) +#define TRACE_CATEGORY_APP (1 << 12) +#define TRACE_CATEGORY_RESOURCES (1 << 13) +#define TRACE_CATEGORY_DALVIK (1 << 14) +#define TRACE_CATEGORY_RS (1 << 15) +#define TRACE_CATEGORY_BIONIC (1 << 16) +#define TRACE_CATEGORY_POWER (1 << 17) +#define TRACE_CATEGORY_PACKAGE_MANAGER (1 << 18) +#define TRACE_CATEGORY_SYSTEM_SERVER (1 << 19) +#define TRACE_CATEGORY_DATABASE (1 << 20) +#define TRACE_CATEGORY_NETWORK (1 << 21) +#define TRACE_CATEGORY_ADB (1 << 22) +#define TRACE_CATEGORY_VIBRATOR (1 << 23) +#define TRACE_CATEGORY_AIDL (1 << 24) +#define TRACE_CATEGORY_NNAPI (1 << 25) +#define TRACE_CATEGORY_RRO (1 << 26) +#define TRACE_CATEGORY_THERMAL (1 << 27) + +// Allow all categories except TRACE_CATEGORY_APP +#define TRACE_CATEGORIES \ + TRACE_CATEGORY_ALWAYS | TRACE_CATEGORY_GRAPHICS | TRACE_CATEGORY_INPUT | \ + TRACE_CATEGORY_VIEW | TRACE_CATEGORY_WEBVIEW | \ + TRACE_CATEGORY_WINDOW_MANAGER | TRACE_CATEGORY_ACTIVITY_MANAGER | \ + TRACE_CATEGORY_SYNC_MANAGER | TRACE_CATEGORY_AUDIO | \ + TRACE_CATEGORY_VIDEO | TRACE_CATEGORY_CAMERA | TRACE_CATEGORY_HAL | \ + TRACE_CATEGORY_RESOURCES | TRACE_CATEGORY_DALVIK | TRACE_CATEGORY_RS | \ + TRACE_CATEGORY_BIONIC | TRACE_CATEGORY_POWER | \ + TRACE_CATEGORY_PACKAGE_MANAGER | TRACE_CATEGORY_SYSTEM_SERVER | \ + TRACE_CATEGORY_DATABASE | TRACE_CATEGORY_NETWORK | TRACE_CATEGORY_ADB | \ + TRACE_CATEGORY_VIBRATOR | TRACE_CATEGORY_AIDL | TRACE_CATEGORY_NNAPI | \ + TRACE_CATEGORY_RRO | TRACE_CATEGORY_THERMAL +#endif // TRACE_CATEGORIES_H diff --git a/libs/tracing_perfetto/include/trace_result.h b/libs/tracing_perfetto/include/trace_result.h new file mode 100644 index 0000000000..f7581fc0fb --- /dev/null +++ b/libs/tracing_perfetto/include/trace_result.h @@ -0,0 +1,30 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TRACE_RESULT_H +#define TRACE_RESULT_H + +namespace tracing_perfetto { + +enum class Result { + SUCCESS, + NOT_SUPPORTED, + INVALID_INPUT, +}; + +} + +#endif // TRACE_RESULT_H diff --git a/libs/tracing_perfetto/include/tracing_perfetto.h b/libs/tracing_perfetto/include/tracing_perfetto.h new file mode 100644 index 0000000000..4e3c83fca3 --- /dev/null +++ b/libs/tracing_perfetto/include/tracing_perfetto.h @@ -0,0 +1,53 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TRACING_PERFETTO_H +#define TRACING_PERFETTO_H + +#include <stdint.h> + +#include "trace_result.h" + +namespace tracing_perfetto { + +void registerWithPerfetto(bool test = false); + +Result traceBegin(uint64_t category, const char* name); + +Result traceEnd(uint64_t category); + +Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie); + +Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie); + +Result traceAsyncBeginForTrack(uint64_t category, const char* name, + const char* trackName, int32_t cookie); + +Result traceAsyncEndForTrack(uint64_t category, const char* trackName, + int32_t cookie); + +Result traceInstant(uint64_t category, const char* name); + +Result traceInstantForTrack(uint64_t category, const char* trackName, + const char* name); + +Result traceCounter(uint64_t category, const char* name, int64_t value); + +uint64_t getEnabledCategories(); + +} // namespace tracing_perfetto + +#endif // TRACING_PERFETTO_H diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp new file mode 100644 index 0000000000..a35b0e0c83 --- /dev/null +++ b/libs/tracing_perfetto/tests/Android.bp @@ -0,0 +1,45 @@ +// Copyright 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_test { + name: "libtracing_perfetto_tests", + static_libs: [ + "libflagtest", + "libgmock", + ], + cflags: [ + "-Wall", + "-Werror", + ], + shared_libs: [ + "android.os.flags-aconfig-cc-host", + "libbase", + "libperfetto_c", + "libtracing_perfetto", + ], + srcs: [ + "tracing_perfetto_test.cpp", + "utils.cpp", + ], + test_suites: ["device-tests"], +} diff --git a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp new file mode 100644 index 0000000000..7716b9a316 --- /dev/null +++ b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "tracing_perfetto.h" + +#include <thread> + +#include <android_os.h> +#include <flag_macros.h> + +#include "gtest/gtest.h" +#include "perfetto/public/abi/data_source_abi.h" +#include "perfetto/public/abi/heap_buffer.h" +#include "perfetto/public/abi/pb_decoder_abi.h" +#include "perfetto/public/abi/tracing_session_abi.h" +#include "perfetto/public/abi/track_event_abi.h" +#include "perfetto/public/data_source.h" +#include "perfetto/public/pb_decoder.h" +#include "perfetto/public/producer.h" +#include "perfetto/public/protos/config/trace_config.pzc.h" +#include "perfetto/public/protos/trace/interned_data/interned_data.pzc.h" +#include "perfetto/public/protos/trace/test_event.pzc.h" +#include "perfetto/public/protos/trace/trace.pzc.h" +#include "perfetto/public/protos/trace/trace_packet.pzc.h" +#include "perfetto/public/protos/trace/track_event/debug_annotation.pzc.h" +#include "perfetto/public/protos/trace/track_event/track_descriptor.pzc.h" +#include "perfetto/public/protos/trace/track_event/track_event.pzc.h" +#include "perfetto/public/protos/trace/trigger.pzc.h" +#include "perfetto/public/te_category_macros.h" +#include "perfetto/public/te_macros.h" +#include "perfetto/public/track_event.h" +#include "trace_categories.h" +#include "utils.h" + +namespace tracing_perfetto { + +using ::perfetto::shlib::test_utils::AllFieldsWithId; +using ::perfetto::shlib::test_utils::FieldView; +using ::perfetto::shlib::test_utils::IdFieldView; +using ::perfetto::shlib::test_utils::MsgField; +using ::perfetto::shlib::test_utils::PbField; +using ::perfetto::shlib::test_utils::StringField; +using ::perfetto::shlib::test_utils::TracingSession; +using ::perfetto::shlib::test_utils::VarIntField; +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::UnorderedElementsAre; + +const auto PERFETTO_SDK_TRACING = ACONFIG_FLAG(android::os, perfetto_sdk_tracing); + +class TracingPerfettoTest : public testing::Test { + protected: + void SetUp() override { + tracing_perfetto::registerWithPerfetto(true /* test */); + } +}; + +// TODO(b/303199244): Add tests for all the library functions. + +TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstant, + REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) { + TracingSession tracing_session = + TracingSession::Builder().set_data_source_name("track_event").Build(); + tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, ""); + + tracing_session.StopBlocking(); + std::vector<uint8_t> data = tracing_session.ReadBlocking(); + bool found = false; + for (struct PerfettoPbDecoderField trace_field : FieldView(data)) { + ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number, + MsgField(_))); + IdFieldView track_event( + trace_field, perfetto_protos_TracePacket_track_event_field_number); + if (track_event.size() == 0) { + continue; + } + found = true; + IdFieldView cat_iid_fields( + track_event.front(), + perfetto_protos_TrackEvent_category_iids_field_number); + ASSERT_THAT(cat_iid_fields, ElementsAre(VarIntField(_))); + uint64_t cat_iid = cat_iid_fields.front().value.integer64; + EXPECT_THAT( + trace_field, + AllFieldsWithId( + perfetto_protos_TracePacket_interned_data_field_number, + ElementsAre(AllFieldsWithId( + perfetto_protos_InternedData_event_categories_field_number, + ElementsAre(MsgField(UnorderedElementsAre( + PbField(perfetto_protos_EventCategory_iid_field_number, + VarIntField(cat_iid)), + PbField(perfetto_protos_EventCategory_name_field_number, + StringField("input"))))))))); + } + EXPECT_TRUE(found); +} + +} // namespace tracing_perfetto
\ No newline at end of file diff --git a/libs/tracing_perfetto/tests/utils.cpp b/libs/tracing_perfetto/tests/utils.cpp new file mode 100644 index 0000000000..9c4202808a --- /dev/null +++ b/libs/tracing_perfetto/tests/utils.cpp @@ -0,0 +1,219 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Copied from //external/perfetto/src/shared_lib/test/utils.cc + +#include "utils.h" + +#include "perfetto/public/abi/heap_buffer.h" +#include "perfetto/public/pb_msg.h" +#include "perfetto/public/pb_utils.h" +#include "perfetto/public/protos/config/data_source_config.pzc.h" +#include "perfetto/public/protos/config/trace_config.pzc.h" +#include "perfetto/public/protos/config/track_event/track_event_config.pzc.h" +#include "perfetto/public/tracing_session.h" + +namespace perfetto { +namespace shlib { +namespace test_utils { +namespace { + +std::string ToHexChars(uint8_t val) { + std::string ret; + uint8_t high_nibble = (val & 0xF0) >> 4; + uint8_t low_nibble = (val & 0xF); + static const char hex_chars[] = "0123456789ABCDEF"; + ret.push_back(hex_chars[high_nibble]); + ret.push_back(hex_chars[low_nibble]); + return ret; +} + +} // namespace + +TracingSession TracingSession::Builder::Build() { + struct PerfettoPbMsgWriter writer; + struct PerfettoHeapBuffer* hb = PerfettoHeapBufferCreate(&writer.writer); + + struct perfetto_protos_TraceConfig cfg; + PerfettoPbMsgInit(&cfg.msg, &writer); + + { + struct perfetto_protos_TraceConfig_BufferConfig buffers; + perfetto_protos_TraceConfig_begin_buffers(&cfg, &buffers); + + perfetto_protos_TraceConfig_BufferConfig_set_size_kb(&buffers, 1024); + + perfetto_protos_TraceConfig_end_buffers(&cfg, &buffers); + } + + { + struct perfetto_protos_TraceConfig_DataSource data_sources; + perfetto_protos_TraceConfig_begin_data_sources(&cfg, &data_sources); + + { + struct perfetto_protos_DataSourceConfig ds_cfg; + perfetto_protos_TraceConfig_DataSource_begin_config(&data_sources, + &ds_cfg); + + perfetto_protos_DataSourceConfig_set_cstr_name(&ds_cfg, + data_source_name_.c_str()); + if (!enabled_categories_.empty() && !disabled_categories_.empty()) { + perfetto_protos_TrackEventConfig te_cfg; + perfetto_protos_DataSourceConfig_begin_track_event_config(&ds_cfg, + &te_cfg); + for (const std::string& cat : enabled_categories_) { + perfetto_protos_TrackEventConfig_set_enabled_categories( + &te_cfg, cat.data(), cat.size()); + } + for (const std::string& cat : disabled_categories_) { + perfetto_protos_TrackEventConfig_set_disabled_categories( + &te_cfg, cat.data(), cat.size()); + } + perfetto_protos_DataSourceConfig_end_track_event_config(&ds_cfg, + &te_cfg); + } + + perfetto_protos_TraceConfig_DataSource_end_config(&data_sources, &ds_cfg); + } + + perfetto_protos_TraceConfig_end_data_sources(&cfg, &data_sources); + } + size_t cfg_size = PerfettoStreamWriterGetWrittenSize(&writer.writer); + std::unique_ptr<uint8_t[]> ser(new uint8_t[cfg_size]); + PerfettoHeapBufferCopyInto(hb, &writer.writer, ser.get(), cfg_size); + PerfettoHeapBufferDestroy(hb, &writer.writer); + + struct PerfettoTracingSessionImpl* ts = + PerfettoTracingSessionCreate(PERFETTO_BACKEND_IN_PROCESS); + + PerfettoTracingSessionSetup(ts, ser.get(), cfg_size); + + PerfettoTracingSessionStartBlocking(ts); + + return TracingSession::Adopt(ts); +} + +TracingSession TracingSession::Adopt(struct PerfettoTracingSessionImpl* session) { + TracingSession ret; + ret.session_ = session; + ret.stopped_ = std::make_unique<WaitableEvent>(); + PerfettoTracingSessionSetStopCb( + ret.session_, + [](struct PerfettoTracingSessionImpl*, void* arg) { + static_cast<WaitableEvent*>(arg)->Notify(); + }, + ret.stopped_.get()); + return ret; +} + +TracingSession::TracingSession(TracingSession&& other) noexcept { + session_ = other.session_; + other.session_ = nullptr; + stopped_ = std::move(other.stopped_); + other.stopped_ = nullptr; +} + +TracingSession::~TracingSession() { + if (!session_) { + return; + } + if (!stopped_->IsNotified()) { + PerfettoTracingSessionStopBlocking(session_); + stopped_->WaitForNotification(); + } + PerfettoTracingSessionDestroy(session_); +} + +bool TracingSession::FlushBlocking(uint32_t timeout_ms) { + WaitableEvent notification; + bool result; + auto* cb = new std::function<void(bool)>([&](bool success) { + result = success; + notification.Notify(); + }); + PerfettoTracingSessionFlushAsync( + session_, timeout_ms, + [](PerfettoTracingSessionImpl*, bool success, void* user_arg) { + auto* f = reinterpret_cast<std::function<void(bool)>*>(user_arg); + (*f)(success); + delete f; + }, + cb); + notification.WaitForNotification(); + return result; +} + +void TracingSession::WaitForStopped() { + stopped_->WaitForNotification(); +} + +void TracingSession::StopBlocking() { + PerfettoTracingSessionStopBlocking(session_); +} + +std::vector<uint8_t> TracingSession::ReadBlocking() { + std::vector<uint8_t> data; + PerfettoTracingSessionReadTraceBlocking( + session_, + [](struct PerfettoTracingSessionImpl*, const void* trace_data, + size_t size, bool, void* user_arg) { + auto& dst = *static_cast<std::vector<uint8_t>*>(user_arg); + auto* src = static_cast<const uint8_t*>(trace_data); + dst.insert(dst.end(), src, src + size); + }, + &data); + return data; +} + +} // namespace test_utils +} // namespace shlib +} // namespace perfetto + +void PrintTo(const PerfettoPbDecoderField& field, std::ostream* pos) { + std::ostream& os = *pos; + PerfettoPbDecoderStatus status = + static_cast<PerfettoPbDecoderStatus>(field.status); + switch (status) { + case PERFETTO_PB_DECODER_ERROR: + os << "MALFORMED PROTOBUF"; + break; + case PERFETTO_PB_DECODER_DONE: + os << "DECODER DONE"; + break; + case PERFETTO_PB_DECODER_OK: + switch (field.wire_type) { + case PERFETTO_PB_WIRE_TYPE_DELIMITED: + os << "\""; + for (size_t i = 0; i < field.value.delimited.len; i++) { + os << perfetto::shlib::test_utils::ToHexChars( + field.value.delimited.start[i]) + << " "; + } + os << "\""; + break; + case PERFETTO_PB_WIRE_TYPE_VARINT: + os << "varint: " << field.value.integer64; + break; + case PERFETTO_PB_WIRE_TYPE_FIXED32: + os << "fixed32: " << field.value.integer32; + break; + case PERFETTO_PB_WIRE_TYPE_FIXED64: + os << "fixed64: " << field.value.integer64; + break; + } + break; + } +} diff --git a/libs/tracing_perfetto/tests/utils.h b/libs/tracing_perfetto/tests/utils.h new file mode 100644 index 0000000000..4353554963 --- /dev/null +++ b/libs/tracing_perfetto/tests/utils.h @@ -0,0 +1,452 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Copied from //external/perfetto/src/shared_lib/test/utils.h + +#ifndef UTILS_H +#define UTILS_H + +#include <cassert> +#include <condition_variable> +#include <cstdint> +#include <functional> +#include <iterator> +#include <memory> +#include <mutex> +#include <ostream> +#include <string> +#include <vector> + +#include "gmock/gmock-matchers.h" +#include "gmock/gmock-more-matchers.h" +#include "gtest/gtest-matchers.h" +#include "gtest/gtest.h" +#include "perfetto/public/abi/pb_decoder_abi.h" +#include "perfetto/public/pb_utils.h" +#include "perfetto/public/tracing_session.h" + +// Pretty printer for gtest +void PrintTo(const PerfettoPbDecoderField& field, std::ostream*); + +namespace perfetto { +namespace shlib { +namespace test_utils { + +class WaitableEvent { + public: + WaitableEvent() = default; + void Notify() { + std::unique_lock<std::mutex> lock(m_); + notified_ = true; + cv_.notify_one(); + } + bool WaitForNotification() { + std::unique_lock<std::mutex> lock(m_); + cv_.wait(lock, [this] { return notified_; }); + return notified_; + } + bool IsNotified() { + std::unique_lock<std::mutex> lock(m_); + return notified_; + } + + private: + std::mutex m_; + std::condition_variable cv_; + bool notified_ = false; +}; + +class TracingSession { + public: + class Builder { + public: + Builder() = default; + Builder& set_data_source_name(std::string data_source_name) { + data_source_name_ = std::move(data_source_name); + return *this; + } + Builder& add_enabled_category(std::string category) { + enabled_categories_.push_back(std::move(category)); + return *this; + } + Builder& add_disabled_category(std::string category) { + disabled_categories_.push_back(std::move(category)); + return *this; + } + TracingSession Build(); + + private: + std::string data_source_name_; + std::vector<std::string> enabled_categories_; + std::vector<std::string> disabled_categories_; + }; + + static TracingSession Adopt(struct PerfettoTracingSessionImpl*); + + TracingSession(TracingSession&&) noexcept; + + ~TracingSession(); + + struct PerfettoTracingSessionImpl* session() const { + return session_; + } + + bool FlushBlocking(uint32_t timeout_ms); + void WaitForStopped(); + void StopBlocking(); + std::vector<uint8_t> ReadBlocking(); + + private: + TracingSession() = default; + struct PerfettoTracingSessionImpl* session_; + std::unique_ptr<WaitableEvent> stopped_; +}; + +template <typename FieldSkipper> +class FieldViewBase { + public: + class Iterator { + public: + using iterator_category = std::input_iterator_tag; + using value_type = const PerfettoPbDecoderField; + using pointer = value_type; + using reference = value_type; + reference operator*() const { + struct PerfettoPbDecoder decoder; + decoder.read_ptr = read_ptr_; + decoder.end_ptr = end_ptr_; + struct PerfettoPbDecoderField field; + do { + field = PerfettoPbDecoderParseField(&decoder); + } while (field.status == PERFETTO_PB_DECODER_OK && + skipper_.ShouldSkip(field)); + return field; + } + Iterator& operator++() { + struct PerfettoPbDecoder decoder; + decoder.read_ptr = read_ptr_; + decoder.end_ptr = end_ptr_; + PerfettoPbDecoderSkipField(&decoder); + read_ptr_ = decoder.read_ptr; + AdvanceToFirstInterestingField(); + return *this; + } + Iterator operator++(int) { + Iterator tmp = *this; + ++(*this); + return tmp; + } + + friend bool operator==(const Iterator& a, const Iterator& b) { + return a.read_ptr_ == b.read_ptr_; + } + friend bool operator!=(const Iterator& a, const Iterator& b) { + return a.read_ptr_ != b.read_ptr_; + } + + private: + Iterator(const uint8_t* read_ptr, const uint8_t* end_ptr, + const FieldSkipper& skipper) + : read_ptr_(read_ptr), end_ptr_(end_ptr), skipper_(skipper) { + AdvanceToFirstInterestingField(); + } + void AdvanceToFirstInterestingField() { + struct PerfettoPbDecoder decoder; + decoder.read_ptr = read_ptr_; + decoder.end_ptr = end_ptr_; + struct PerfettoPbDecoderField field; + const uint8_t* prev_read_ptr; + do { + prev_read_ptr = decoder.read_ptr; + field = PerfettoPbDecoderParseField(&decoder); + } while (field.status == PERFETTO_PB_DECODER_OK && + skipper_.ShouldSkip(field)); + if (field.status == PERFETTO_PB_DECODER_OK) { + read_ptr_ = prev_read_ptr; + } else { + read_ptr_ = decoder.read_ptr; + } + } + friend class FieldViewBase<FieldSkipper>; + const uint8_t* read_ptr_; + const uint8_t* end_ptr_; + const FieldSkipper& skipper_; + }; + using value_type = const PerfettoPbDecoderField; + using const_iterator = Iterator; + template <typename... Args> + explicit FieldViewBase(const uint8_t* begin, const uint8_t* end, Args... args) + : begin_(begin), end_(end), s_(args...) { + } + template <typename... Args> + explicit FieldViewBase(const std::vector<uint8_t>& data, Args... args) + : FieldViewBase(data.data(), data.data() + data.size(), args...) { + } + template <typename... Args> + explicit FieldViewBase(const struct PerfettoPbDecoderField& field, + Args... args) + : s_(args...) { + if (field.wire_type != PERFETTO_PB_WIRE_TYPE_DELIMITED) { + abort(); + } + begin_ = field.value.delimited.start; + end_ = begin_ + field.value.delimited.len; + } + Iterator begin() const { + return Iterator(begin_, end_, s_); + } + Iterator end() const { + return Iterator(end_, end_, s_); + } + PerfettoPbDecoderField front() const { + return *begin(); + } + + size_t size() const { + size_t count = 0; + for (auto field : *this) { + (void)field; + count++; + } + return count; + } + + bool ok() const { + for (auto field : *this) { + if (field.status != PERFETTO_PB_DECODER_OK) { + return false; + } + } + return true; + } + + private: + const uint8_t* begin_; + const uint8_t* end_; + FieldSkipper s_; +}; + +// Pretty printer for gtest +template <typename FieldSkipper> +void PrintTo(const FieldViewBase<FieldSkipper>& field_view, std::ostream* pos) { + std::ostream& os = *pos; + os << "{"; + for (PerfettoPbDecoderField f : field_view) { + PrintTo(f, pos); + os << ", "; + } + os << "}"; +} + +class IdFieldSkipper { + public: + explicit IdFieldSkipper(uint32_t id) : id_(id) { + } + explicit IdFieldSkipper(int32_t id) : id_(static_cast<uint32_t>(id)) { + } + bool ShouldSkip(const struct PerfettoPbDecoderField& field) const { + return field.id != id_; + } + + private: + uint32_t id_; +}; + +class NoFieldSkipper { + public: + NoFieldSkipper() = default; + bool ShouldSkip(const struct PerfettoPbDecoderField&) const { + return false; + } +}; + +// View over all the fields of a contiguous serialized protobuf message. +// +// Examples: +// +// for (struct PerfettoPbDecoderField field : FieldView(msg_begin, msg_end)) { +// //... +// } +// FieldView fields2(/*PerfettoPbDecoderField*/ nested_field); +// FieldView fields3(/*std::vector<uint8_t>*/ data); +// size_t num = fields1.size(); // The number of fields. +// bool ok = fields1.ok(); // Checks that the message is not malformed. +using FieldView = FieldViewBase<NoFieldSkipper>; + +// Like `FieldView`, but only considers fields with a specific id. +// +// Examples: +// +// IdFieldView fields(msg_begin, msg_end, id) +using IdFieldView = FieldViewBase<IdFieldSkipper>; + +// Matches a PerfettoPbDecoderField with the specified id. Accepts another +// matcher to match the contents of the field. +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, PbField(900, VarIntField(5))); +template <typename M> +auto PbField(int32_t id, M m) { + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::id, id), m); +} + +// Matches a PerfettoPbDecoderField submessage field. Accepts a container +// matcher for the subfields. +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, MsgField(ElementsAre(...))); +template <typename M> +auto MsgField(M m) { + auto f = [](const PerfettoPbDecoderField& field) { return FieldView(field); }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_DELIMITED), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField length delimited field. Accepts a string +// matcher. +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, StringField("string")); +template <typename M> +auto StringField(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return std::string( + reinterpret_cast<const char*>(field.value.delimited.start), + field.value.delimited.len); + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_DELIMITED), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField VarInt field. Accepts an integer matcher +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, VarIntField(1))); +template <typename M> +auto VarIntField(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return field.value.integer64; + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_VARINT), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField fixed64 field. Accepts an integer matcher +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, Fixed64Field(1))); +template <typename M> +auto Fixed64Field(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return field.value.integer64; + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_FIXED64), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField fixed32 field. Accepts an integer matcher +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, Fixed32Field(1))); +template <typename M> +auto Fixed32Field(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return field.value.integer32; + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_FIXED32), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField double field. Accepts a double matcher +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, DoubleField(1.0))); +template <typename M> +auto DoubleField(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return field.value.double_val; + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_FIXED64), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField float field. Accepts a float matcher +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, FloatField(1.0))); +template <typename M> +auto FloatField(M m) { + auto f = [](const PerfettoPbDecoderField& field) { + return field.value.float_val; + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_FIXED32), + testing::ResultOf(f, m)); +} + +// Matches a PerfettoPbDecoderField submessage field. Accepts a container +// matcher for the subfields. +// +// Example: +// PerfettoPbDecoderField field = ... +// EXPECT_THAT(field, AllFieldsWithId(900, ElementsAre(...))); +template <typename M> +auto AllFieldsWithId(int32_t id, M m) { + auto f = [id](const PerfettoPbDecoderField& field) { + return IdFieldView(field, id); + }; + return testing::AllOf( + testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK), + testing::Field(&PerfettoPbDecoderField::wire_type, + PERFETTO_PB_WIRE_TYPE_DELIMITED), + testing::ResultOf(f, m)); +} + +} // namespace test_utils +} // namespace shlib +} // namespace perfetto + +#endif // UTILS_H diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp new file mode 100644 index 0000000000..c7fb8bd9a8 --- /dev/null +++ b/libs/tracing_perfetto/tracing_perfetto.cpp @@ -0,0 +1,141 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "tracing_perfetto.h" + +#include <cutils/trace.h> + +#include "perfetto/public/te_category_macros.h" +#include "trace_categories.h" +#include "tracing_perfetto_internal.h" + +namespace tracing_perfetto { + +void registerWithPerfetto(bool test) { + internal::registerWithPerfetto(test); +} + +Result traceBegin(uint64_t category, const char* name) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceBegin(*perfettoTeCategory, name); + } else { + atrace_begin(category, name); + return Result::SUCCESS; + } +} + +Result traceEnd(uint64_t category) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceEnd(*perfettoTeCategory); + } else { + atrace_end(category); + return Result::SUCCESS; + } +} + +Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceAsyncBegin(*perfettoTeCategory, name, cookie); + } else { + atrace_async_begin(category, name, cookie); + return Result::SUCCESS; + } +} + +Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceAsyncEnd(*perfettoTeCategory, name, cookie); + } else { + atrace_async_end(category, name, cookie); + return Result::SUCCESS; + } +} + +Result traceAsyncBeginForTrack(uint64_t category, const char* name, + const char* trackName, int32_t cookie) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie); + } else { + atrace_async_for_track_begin(category, trackName, name, cookie); + return Result::SUCCESS; + } +} + +Result traceAsyncEndForTrack(uint64_t category, const char* trackName, + int32_t cookie) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie); + } else { + atrace_async_for_track_end(category, trackName, cookie); + return Result::SUCCESS; + } +} + +Result traceInstant(uint64_t category, const char* name) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceInstant(*perfettoTeCategory, name); + } else { + atrace_instant(category, name); + return Result::SUCCESS; + } +} + +Result traceInstantForTrack(uint64_t category, const char* trackName, + const char* name) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceInstantForTrack(*perfettoTeCategory, trackName, name); + } else { + atrace_instant_for_track(category, trackName, name); + return Result::SUCCESS; + } +} + +Result traceCounter(uint64_t category, const char* name, int64_t value) { + struct PerfettoTeCategory* perfettoTeCategory = + internal::toPerfettoCategory(category); + if (perfettoTeCategory != nullptr) { + return internal::perfettoTraceCounter(*perfettoTeCategory, name, value); + } else { + atrace_int64(category, name, value); + return Result::SUCCESS; + } +} + +uint64_t getEnabledCategories() { + if (internal::isPerfettoSdkTracingEnabled()) { + return internal::getDefaultCategories(); + } else { + return atrace_get_enabled_tags(); + } +} + +} // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp new file mode 100644 index 0000000000..58ba428610 --- /dev/null +++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp @@ -0,0 +1,229 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define FRAMEWORK_CATEGORIES(C) \ + C(always, "always", "Always category") \ + C(graphics, "graphics", "Graphics category") \ + C(input, "input", "Input category") \ + C(view, "view", "View category") \ + C(webview, "webview", "WebView category") \ + C(windowmanager, "wm", "WindowManager category") \ + C(activitymanager, "am", "ActivityManager category") \ + C(syncmanager, "syncmanager", "SyncManager category") \ + C(audio, "audio", "Audio category") \ + C(video, "video", "Video category") \ + C(camera, "camera", "Camera category") \ + C(hal, "hal", "HAL category") \ + C(app, "app", "App category") \ + C(resources, "res", "Resources category") \ + C(dalvik, "dalvik", "Dalvik category") \ + C(rs, "rs", "RS category") \ + C(bionic, "bionic", "Bionic category") \ + C(power, "power", "Power category") \ + C(packagemanager, "packagemanager", "PackageManager category") \ + C(systemserver, "ss", "System Server category") \ + C(database, "database", "Database category") \ + C(network, "network", "Network category") \ + C(adb, "adb", "ADB category") \ + C(vibrator, "vibrator", "Vibrator category") \ + C(aidl, "aidl", "AIDL category") \ + C(nnapi, "nnapi", "NNAPI category") \ + C(rro, "rro", "RRO category") \ + C(thermal, "thermal", "Thermal category") + +#include "tracing_perfetto_internal.h" + +#include <inttypes.h> + +#include <mutex> + +#include <android_os.h> + +#include "perfetto/public/compiler.h" +#include "perfetto/public/producer.h" +#include "perfetto/public/te_category_macros.h" +#include "perfetto/public/te_macros.h" +#include "perfetto/public/track_event.h" +#include "trace_categories.h" +#include "trace_result.h" + +namespace tracing_perfetto { + +namespace internal { + +namespace { + +PERFETTO_TE_CATEGORIES_DECLARE(FRAMEWORK_CATEGORIES); + +PERFETTO_TE_CATEGORIES_DEFINE(FRAMEWORK_CATEGORIES); + +struct PerfettoTeCategory* toCategory(uint64_t inCategory) { + switch (inCategory) { + case TRACE_CATEGORY_ALWAYS: + return &always; + case TRACE_CATEGORY_GRAPHICS: + return &graphics; + case TRACE_CATEGORY_INPUT: + return &input; + case TRACE_CATEGORY_VIEW: + return &view; + case TRACE_CATEGORY_WEBVIEW: + return &webview; + case TRACE_CATEGORY_WINDOW_MANAGER: + return &windowmanager; + case TRACE_CATEGORY_ACTIVITY_MANAGER: + return &activitymanager; + case TRACE_CATEGORY_SYNC_MANAGER: + return &syncmanager; + case TRACE_CATEGORY_AUDIO: + return &audio; + case TRACE_CATEGORY_VIDEO: + return &video; + case TRACE_CATEGORY_CAMERA: + return &camera; + case TRACE_CATEGORY_HAL: + return &hal; + case TRACE_CATEGORY_APP: + return &app; + case TRACE_CATEGORY_RESOURCES: + return &resources; + case TRACE_CATEGORY_DALVIK: + return &dalvik; + case TRACE_CATEGORY_RS: + return &rs; + case TRACE_CATEGORY_BIONIC: + return &bionic; + case TRACE_CATEGORY_POWER: + return &power; + case TRACE_CATEGORY_PACKAGE_MANAGER: + return &packagemanager; + case TRACE_CATEGORY_SYSTEM_SERVER: + return &systemserver; + case TRACE_CATEGORY_DATABASE: + return &database; + case TRACE_CATEGORY_NETWORK: + return &network; + case TRACE_CATEGORY_ADB: + return &adb; + case TRACE_CATEGORY_VIBRATOR: + return &vibrator; + case TRACE_CATEGORY_AIDL: + return &aidl; + case TRACE_CATEGORY_NNAPI: + return &nnapi; + case TRACE_CATEGORY_RRO: + return &rro; + case TRACE_CATEGORY_THERMAL: + return &thermal; + default: + return nullptr; + } +} + +} // namespace + +bool isPerfettoSdkTracingEnabled() { + return android::os::perfetto_sdk_tracing(); +} + +struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) { + if (!isPerfettoSdkTracingEnabled()) { + return nullptr; + } + + struct PerfettoTeCategory* perfettoCategory = toCategory(category); + bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT( + (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED)); + return enabled ? perfettoCategory : nullptr; +} + +void registerWithPerfetto(bool test) { + if (!isPerfettoSdkTracingEnabled()) { + return; + } + static std::once_flag registration; + std::call_once(registration, [test]() { + struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT(); + args.backends = test ? PERFETTO_BACKEND_IN_PROCESS : PERFETTO_BACKEND_SYSTEM; + PerfettoProducerInit(args); + PerfettoTeInit(); + PERFETTO_TE_REGISTER_CATEGORIES(FRAMEWORK_CATEGORIES); + }); +} + +Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name) { + PERFETTO_TE(category, PERFETTO_TE_SLICE_BEGIN(name)); + return Result::SUCCESS; +} + +Result perfettoTraceEnd(const struct PerfettoTeCategory& category) { + PERFETTO_TE(category, PERFETTO_TE_SLICE_END()); + return Result::SUCCESS; +} + +Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name, + const char* trackName, uint64_t cookie) { + PERFETTO_TE( + category, PERFETTO_TE_SLICE_BEGIN(name), + PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); + return Result::SUCCESS; +} + +Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category, + const char* trackName, uint64_t cookie) { + PERFETTO_TE( + category, PERFETTO_TE_SLICE_END(), + PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); + return Result::SUCCESS; +} + +Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name, + uint64_t cookie) { + return perfettoTraceAsyncBeginForTrack(category, name, name, cookie); +} + +Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name, + uint64_t cookie) { + return perfettoTraceAsyncEndForTrack(category, name, cookie); +} + +Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name) { + PERFETTO_TE(category, PERFETTO_TE_INSTANT(name)); + return Result::SUCCESS; +} + +Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category, + const char* trackName, const char* name) { + PERFETTO_TE( + category, PERFETTO_TE_INSTANT(name), + PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid())); + return Result::SUCCESS; +} + +Result perfettoTraceCounter(const struct PerfettoTeCategory& category, + [[maybe_unused]] const char* name, int64_t value) { + PERFETTO_TE(category, PERFETTO_TE_COUNTER(), + PERFETTO_TE_INT_COUNTER(value)); + return Result::SUCCESS; +} + +uint64_t getDefaultCategories() { + return TRACE_CATEGORIES; +} + +} // namespace internal + +} // namespace tracing_perfetto diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h new file mode 100644 index 0000000000..9a579f162a --- /dev/null +++ b/libs/tracing_perfetto/tracing_perfetto_internal.h @@ -0,0 +1,65 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TRACING_PERFETTO_INTERNAL_H +#define TRACING_PERFETTO_INTERNAL_H + +#include <stdint.h> + +#include "include/trace_result.h" +#include "perfetto/public/te_category_macros.h" + +namespace tracing_perfetto { + +namespace internal { + +bool isPerfettoSdkTracingEnabled(); + +struct PerfettoTeCategory* toPerfettoCategory(uint64_t category); + +void registerWithPerfetto(bool test = false); + +Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name); + +Result perfettoTraceEnd(const struct PerfettoTeCategory& category); + +Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name, + uint64_t cookie); + +Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name, + uint64_t cookie); + +Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name, + const char* trackName, uint64_t cookie); + +Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category, + const char* trackName, uint64_t cookie); + +Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name); + +Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category, + const char* trackName, const char* name); + +Result perfettoTraceCounter(const struct PerfettoTeCategory& category, const char* name, + int64_t value); + +uint64_t getDefaultCategories(); + +} // namespace internal + +} // namespace tracing_perfetto + +#endif // TRACING_PERFETTO_INTERNAL_H diff --git a/services/inputflinger/InputProcessor.h b/services/inputflinger/InputProcessor.h index dcbfebc62f..7a00a2dae8 100644 --- a/services/inputflinger/InputProcessor.h +++ b/services/inputflinger/InputProcessor.h @@ -22,7 +22,7 @@ #include <unordered_map> #include <aidl/android/hardware/input/processor/IInputProcessor.h> -#include "BlockingQueue.h" +#include <input/BlockingQueue.h> #include "InputListener.h" namespace android { diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 9db3574389..c333814078 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -122,21 +122,32 @@ NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotio } auto [displayId, pc] = ensureMouseControllerLocked(args.displayId); + NotifyMotionArgs newArgs(args); + newArgs.displayId = displayId; - const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - pc.move(deltaX, deltaY); + if (MotionEvent::isValidCursorPosition(args.xCursorPosition, args.yCursorPosition)) { + // This is an absolute mouse device that knows about the location of the cursor on the + // display, so set the cursor position to the specified location. + const auto [x, y] = pc.getPosition(); + const float deltaX = args.xCursorPosition - x; + const float deltaY = args.yCursorPosition - y; + newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); + newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); + pc.setPosition(args.xCursorPosition, args.yCursorPosition); + } else { + // This is a relative mouse, so move the cursor by the specified amount. + const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + pc.move(deltaX, deltaY); + const auto [x, y] = pc.getPosition(); + newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + newArgs.xCursorPosition = x; + newArgs.yCursorPosition = y; + } if (canUnfadeOnDisplay(displayId)) { pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); } - - const auto [x, y] = pc.getPosition(); - NotifyMotionArgs newArgs(args); - newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - newArgs.xCursorPosition = x; - newArgs.yCursorPosition = y; - newArgs.displayId = displayId; return newArgs; } @@ -277,7 +288,7 @@ void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) void PointerChoreographer::notifyPointerCaptureChanged( const NotifyPointerCaptureChangedArgs& args) { - if (args.request.enable) { + if (args.request.isEnable()) { std::scoped_lock _l(mLock); for (const auto& [_, mousePointerController] : mMousePointersByDisplay) { mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h index 83e6a60602..9c73f03dc8 100644 --- a/services/inputflinger/dispatcher/CancelationOptions.h +++ b/services/inputflinger/dispatcher/CancelationOptions.h @@ -16,6 +16,8 @@ #pragma once +#include "trace/EventTrackerInterface.h" + #include <input/Input.h> #include <bitset> #include <optional> @@ -51,7 +53,13 @@ struct CancelationOptions { // The specific pointers to cancel, or nullopt to cancel all pointer events std::optional<std::bitset<MAX_POINTER_ID + 1>> pointerIds = std::nullopt; - CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) {} + const std::unique_ptr<trace::EventTrackerInterface>& traceTracker; + + explicit CancelationOptions(Mode mode, const char* reason, + const std::unique_ptr<trace::EventTrackerInterface>& traceTracker) + : mode(mode), reason(reason), traceTracker(traceTracker) {} + CancelationOptions(const CancelationOptions&) = delete; + CancelationOptions operator=(const CancelationOptions&) = delete; }; } // namespace inputdispatcher diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 264dc03e70..0246d606ac 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -110,7 +110,7 @@ PointerCaptureChangedEntry::PointerCaptureChangedEntry(int32_t id, nsecs_t event std::string PointerCaptureChangedEntry::getDescription() const { return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)", - pointerCaptureRequest.enable ? "true" : "false"); + pointerCaptureRequest.isEnable() ? "true" : "false"); } // --- DragEntry --- diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 73bbed6c68..dc220fe57e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -399,7 +399,8 @@ std::unique_ptr<DispatchEntry> createDispatchEntry(const IdGenerator& idGenerato const InputTarget& inputTarget, std::shared_ptr<const EventEntry> eventEntry, ftl::Flags<InputTarget::Flags> inputTargetFlags, - int64_t vsyncId) { + int64_t vsyncId, + trace::InputTracerInterface* tracer) { const bool zeroCoords = inputTargetFlags.test(InputTarget::Flags::ZERO_COORDS); const sp<WindowInfoHandle> win = inputTarget.windowHandle; const std::optional<int32_t> windowId = @@ -462,6 +463,10 @@ std::unique_ptr<DispatchEntry> createDispatchEntry(const IdGenerator& idGenerato motionEntry.xCursorPosition, motionEntry.yCursorPosition, motionEntry.downTime, motionEntry.pointerProperties, pointerCoords); + if (tracer) { + combinedMotionEntry->traceTracker = + tracer->traceDerivedEvent(*combinedMotionEntry, *motionEntry.traceTracker); + } std::unique_ptr<DispatchEntry> dispatchEntry = std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags, @@ -857,6 +862,30 @@ std::pair<bool /*cancelPointers*/, bool /*cancelNonPointers*/> expandCancellatio } } +class ScopedSyntheticEventTracer { +public: + ScopedSyntheticEventTracer(std::unique_ptr<trace::InputTracerInterface>& tracer) + : mTracer(tracer) { + if (mTracer) { + mEventTracker = mTracer->createTrackerForSyntheticEvent(); + } + } + + ~ScopedSyntheticEventTracer() { + if (mTracer) { + mTracer->eventProcessingComplete(*mEventTracker); + } + } + + const std::unique_ptr<trace::EventTrackerInterface>& getTracker() const { + return mEventTracker; + } + +private: + std::unique_ptr<trace::InputTracerInterface>& mTracer; + std::unique_ptr<trace::EventTrackerInterface> mEventTracker; +}; + } // namespace // --- InputDispatcher --- @@ -1474,8 +1503,9 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason switch (entry.type) { case EventEntry::Type::KEY: { - CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason); const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry); + CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason, + keyEntry.traceTracker); options.displayId = keyEntry.displayId; options.deviceId = keyEntry.deviceId; synthesizeCancelationEventsForAllConnectionsLocked(options); @@ -1484,13 +1514,14 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason case EventEntry::Type::MOTION: { const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry); if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) { - CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason); + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason, + motionEntry.traceTracker); options.displayId = motionEntry.displayId; options.deviceId = motionEntry.deviceId; synthesizeCancelationEventsForAllConnectionsLocked(options); } else { CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, - reason); + reason, motionEntry.traceTracker); options.displayId = motionEntry.displayId; options.deviceId = motionEntry.deviceId; synthesizeCancelationEventsForAllConnectionsLocked(options); @@ -1625,7 +1656,9 @@ bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, resetKeyRepeatLocked(); } - CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "device was reset"); + ScopedSyntheticEventTracer traceContext(mTracer); + CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "device was reset", + traceContext.getTracker()); options.deviceId = entry.deviceId; synthesizeCancelationEventsForAllConnectionsLocked(options); @@ -1682,7 +1715,7 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr; sp<IBinder> token; - if (entry->pointerCaptureRequest.enable) { + if (entry->pointerCaptureRequest.isEnable()) { // Enable Pointer Capture. if (haveWindowWithPointerCapture && (entry->pointerCaptureRequest == mCurrentPointerCaptureRequest)) { @@ -1691,7 +1724,7 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( ALOGI("Skipping dispatch of Pointer Capture being enabled: no state change."); return; } - if (!mCurrentPointerCaptureRequest.enable) { + if (!mCurrentPointerCaptureRequest.isEnable()) { // This can happen if a window requests capture and immediately releases capture. ALOGW("No window requested Pointer Capture."); dropReason = DropReason::NO_POINTER_CAPTURE; @@ -1704,6 +1737,8 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId); LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture."); + LOG_ALWAYS_FATAL_IF(token != entry->pointerCaptureRequest.window, + "Unexpected requested window for Pointer Capture."); mWindowTokenWithPointerCapture = token; } else { // Disable Pointer Capture. @@ -1723,8 +1758,8 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( } token = mWindowTokenWithPointerCapture; mWindowTokenWithPointerCapture = nullptr; - if (mCurrentPointerCaptureRequest.enable) { - setPointerCaptureLocked(false); + if (mCurrentPointerCaptureRequest.isEnable()) { + setPointerCaptureLocked(nullptr); } } @@ -1732,8 +1767,8 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( if (connection == nullptr) { // Window has gone away, clean up Pointer Capture state. mWindowTokenWithPointerCapture = nullptr; - if (mCurrentPointerCaptureRequest.enable) { - setPointerCaptureLocked(false); + if (mCurrentPointerCaptureRequest.isEnable()) { + setPointerCaptureLocked(nullptr); } return; } @@ -1780,7 +1815,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con DropReason* dropReason, nsecs_t& nextWakeupTime) { // Preprocessing. if (!entry->dispatchInProgress) { - if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN && + if (!entry->syntheticRepeat && entry->action == AKEY_EVENT_ACTION_DOWN && (entry->policyFlags & POLICY_FLAG_TRUSTED) && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) { if (mKeyRepeatState.lastKeyEntry && @@ -2014,7 +2049,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, CancelationOptions::Mode mode( isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS : CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS); - CancelationOptions options(mode, "input event injection failed"); + CancelationOptions options(mode, "input event injection failed", entry->traceTracker); options.displayId = entry->displayId; synthesizeCancelationEventsForMonitorsLocked(options); return true; @@ -2120,8 +2155,9 @@ void InputDispatcher::cancelEventsForAnrLocked(const std::shared_ptr<Connection> if (connection->status != Connection::Status::NORMAL) { return; } + ScopedSyntheticEventTracer traceContext(mTracer); CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, - "application not responding"); + "application not responding", traceContext.getTracker()); sp<WindowInfoHandle> windowHandle; if (!connection->monitor) { @@ -3386,7 +3422,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio // Enqueue a new dispatch entry onto the outbound queue for this connection. std::unique_ptr<DispatchEntry> dispatchEntry = createDispatchEntry(mIdGenerator, inputTarget, eventEntry, inputTarget.flags, - mWindowInfosVsyncId); + mWindowInfosVsyncId, mTracer.get()); // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a // different EventEntry than what was passed in. @@ -3469,21 +3505,31 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio usingCoords = pointerInfo->second; } } - // Generate a new MotionEntry with a new eventId using the resolved action and - // flags. - resolvedMotion = std::make_shared< - MotionEntry>(mIdGenerator.nextId(), motionEntry.injectionState, - motionEntry.eventTime, motionEntry.deviceId, - motionEntry.source, motionEntry.displayId, - motionEntry.policyFlags, resolvedAction, - motionEntry.actionButton, resolvedFlags, - motionEntry.metaState, motionEntry.buttonState, - motionEntry.classification, motionEntry.edgeFlags, - motionEntry.xPrecision, motionEntry.yPrecision, - motionEntry.xCursorPosition, motionEntry.yCursorPosition, - motionEntry.downTime, - usingProperties.value_or(motionEntry.pointerProperties), - usingCoords.value_or(motionEntry.pointerCoords)); + { + // Generate a new MotionEntry with a new eventId using the resolved action + // and flags, and set it as the resolved entry. + auto newEntry = std::make_shared< + MotionEntry>(mIdGenerator.nextId(), motionEntry.injectionState, + motionEntry.eventTime, motionEntry.deviceId, + motionEntry.source, motionEntry.displayId, + motionEntry.policyFlags, resolvedAction, + motionEntry.actionButton, resolvedFlags, + motionEntry.metaState, motionEntry.buttonState, + motionEntry.classification, motionEntry.edgeFlags, + motionEntry.xPrecision, motionEntry.yPrecision, + motionEntry.xCursorPosition, + motionEntry.yCursorPosition, motionEntry.downTime, + usingProperties.value_or( + motionEntry.pointerProperties), + usingCoords.value_or(motionEntry.pointerCoords)); + if (mTracer) { + ensureEventTraced(motionEntry); + newEntry->traceTracker = + mTracer->traceDerivedEvent(*newEntry, + *motionEntry.traceTracker); + } + resolvedMotion = newEntry; + } if (ATRACE_ENABLED()) { std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32 ") to MotionEvent(id=0x%" PRIx32 ").", @@ -3506,9 +3552,14 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio LOG(INFO) << "Canceling pointers for device " << resolvedMotion->deviceId << " in " << connection->getInputChannelName() << " with event " << cancelEvent->getDescription(); + if (mTracer) { + static_cast<MotionEntry&>(*cancelEvent).traceTracker = + mTracer->traceDerivedEvent(*cancelEvent, *resolvedMotion->traceTracker); + } std::unique_ptr<DispatchEntry> cancelDispatchEntry = createDispatchEntry(mIdGenerator, inputTarget, std::move(cancelEvent), - ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId); + ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId, + mTracer.get()); // Send these cancel events to the queue before sending the event from the new // device. @@ -3742,7 +3793,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, keyEntry.metaState, keyEntry.repeatCount, keyEntry.downTime, keyEntry.eventTime); if (mTracer) { - mTracer->traceEventDispatch(*dispatchEntry, keyEntry.traceTracker.get()); + ensureEventTraced(keyEntry); + mTracer->traceEventDispatch(*dispatchEntry, *keyEntry.traceTracker); } break; } @@ -3755,7 +3807,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); status = publishMotionEvent(*connection, *dispatchEntry); if (mTracer) { - mTracer->traceEventDispatch(*dispatchEntry, motionEntry.traceTracker.get()); + ensureEventTraced(motionEntry); + mTracer->traceEventDispatch(*dispatchEntry, *motionEntry.traceTracker); } break; } @@ -3781,9 +3834,10 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, case EventEntry::Type::POINTER_CAPTURE_CHANGED: { const auto& captureEntry = static_cast<const PointerCaptureChangedEntry&>(eventEntry); - status = connection->inputPublisher - .publishCaptureEvent(dispatchEntry->seq, captureEntry.id, - captureEntry.pointerCaptureRequest.enable); + status = + connection->inputPublisher + .publishCaptureEvent(dispatchEntry->seq, captureEntry.id, + captureEntry.pointerCaptureRequest.isEnable()); break; } @@ -4134,6 +4188,11 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( switch (cancelationEventEntry->type) { case EventEntry::Type::KEY: { + if (mTracer) { + static_cast<KeyEntry&>(*cancelationEventEntry).traceTracker = + mTracer->traceDerivedEvent(*cancelationEventEntry, + *options.traceTracker); + } const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry); if (window) { addWindowTargetLocked(window, InputTarget::DispatchMode::AS_IS, @@ -4145,6 +4204,11 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( break; } case EventEntry::Type::MOTION: { + if (mTracer) { + static_cast<MotionEntry&>(*cancelationEventEntry).traceTracker = + mTracer->traceDerivedEvent(*cancelationEventEntry, + *options.traceTracker); + } const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry); if (window) { std::bitset<MAX_POINTER_ID + 1> pointerIds; @@ -4192,6 +4256,9 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created"; + if (mTracer) { + mTracer->dispatchToTargetHint(*options.traceTracker, targets[0]); + } enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), targets[0]); } @@ -4203,7 +4270,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( const nsecs_t downTime, const std::shared_ptr<Connection>& connection, - ftl::Flags<InputTarget::Flags> targetFlags) { + ftl::Flags<InputTarget::Flags> targetFlags, + const std::unique_ptr<trace::EventTrackerInterface>& traceTracker) { if (connection->status != Connection::Status::NORMAL) { return; } @@ -4232,6 +4300,10 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( std::vector<InputTarget> targets{}; switch (downEventEntry->type) { case EventEntry::Type::MOTION: { + if (mTracer) { + static_cast<MotionEntry&>(*downEventEntry).traceTracker = + mTracer->traceDerivedEvent(*downEventEntry, *traceTracker); + } const auto& motionEntry = static_cast<const MotionEntry&>(*downEventEntry); if (windowHandle != nullptr) { std::bitset<MAX_POINTER_ID + 1> pointerIds; @@ -4269,6 +4341,9 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( } if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created"; + if (mTracer) { + mTracer->dispatchToTargetHint(*traceTracker, targets[0]); + } enqueueDispatchEntryLocked(connection, std::move(downEventEntry), targets[0]); } @@ -4285,6 +4360,20 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( MotionEvent::split(originalMotionEntry.action, originalMotionEntry.flags, /*historySize=*/0, originalMotionEntry.pointerProperties, originalMotionEntry.pointerCoords, pointerIds); + if (pointerIds.count() != pointerCoords.size()) { + // TODO(b/329107108): Determine why some IDs in pointerIds were not in originalMotionEntry. + // This is bad. We are missing some of the pointers that we expected to deliver. + // Most likely this indicates that we received an ACTION_MOVE events that has + // different pointer ids than we expected based on the previous ACTION_DOWN + // or ACTION_POINTER_DOWN events that caused us to decide to split the pointers + // in this way. + ALOGW("Dropping split motion event because the pointer count is %d but " + "we expected there to be %zu pointers. This probably means we received " + "a broken sequence of pointer ids from the input device: %s", + pointerCoords.size(), pointerIds.count(), + originalMotionEntry.getDescription().c_str()); + return nullptr; + } // TODO(b/327503168): Move this check inside MotionEvent::split once all callers handle it // correctly. @@ -4316,6 +4405,10 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( originalMotionEntry.xCursorPosition, originalMotionEntry.yCursorPosition, splitDownTime, pointerProperties, pointerCoords); + if (mTracer) { + splitMotionEntry->traceTracker = + mTracer->traceDerivedEvent(*splitMotionEntry, *originalMotionEntry.traceTracker); + } return splitMotionEntry; } @@ -4637,7 +4730,7 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs& args) { void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) { if (debugInboundEventDetails()) { ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args.eventTime, - args.request.enable ? "true" : "false"); + args.request.isEnable() ? "true" : "false"); } bool needWake = false; @@ -5202,6 +5295,7 @@ void InputDispatcher::setInputWindowsLocked( } LOG(INFO) << "setInputWindows displayId=" << displayId << " " << windowList; } + ScopedSyntheticEventTracer traceContext(mTracer); // Check preconditions for new input windows for (const sp<WindowInfoHandle>& window : windowInfoHandles) { @@ -5241,7 +5335,7 @@ void InputDispatcher::setInputWindowsLocked( std::optional<FocusResolver::FocusChanges> changes = mFocusResolver.setInputWindows(displayId, windowHandles); if (changes) { - onFocusChangedLocked(*changes, removedFocusedWindowHandle); + onFocusChangedLocked(*changes, traceContext.getTracker(), removedFocusedWindowHandle); } std::unordered_map<int32_t, TouchState>::iterator stateIt = @@ -5254,7 +5348,7 @@ void InputDispatcher::setInputWindowsLocked( LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName() << " in display %" << displayId; CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, - "touched window was removed"); + "touched window was removed", traceContext.getTracker()); synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options); // Since we are about to drop the touch, cancel the events for the wallpaper as // well. @@ -5355,6 +5449,7 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { } { // acquire lock std::scoped_lock _l(mLock); + ScopedSyntheticEventTracer traceContext(mTracer); if (mFocusedDisplayId != displayId) { sp<IBinder> oldFocusedWindowToken = @@ -5367,7 +5462,8 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { } CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, - "The display which contains this window no longer has focus."); + "The display which contains this window no longer has focus.", + traceContext.getTracker()); options.displayId = ADISPLAY_ID_NONE; synthesizeCancelationEventsForWindowLocked(windowHandle, options); } @@ -5592,19 +5688,22 @@ bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const s } // Synthesize cancel for old window and down for new window. + ScopedSyntheticEventTracer traceContext(mTracer); std::shared_ptr<Connection> fromConnection = getConnectionLocked(fromToken); std::shared_ptr<Connection> toConnection = getConnectionLocked(toToken); if (fromConnection != nullptr && toConnection != nullptr) { fromConnection->inputState.mergePointerStateTo(toConnection->inputState); CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, - "transferring touch from this window to another window"); + "transferring touch from this window to another window", + traceContext.getTracker()); synthesizeCancelationEventsForWindowLocked(fromWindowHandle, options, fromConnection); synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection, - newTargetFlags); + newTargetFlags, + traceContext.getTracker()); // Check if the wallpaper window should deliver the corresponding event. transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle, - *state, deviceId, pointers); + *state, deviceId, pointers, traceContext.getTracker()); } } // release lock @@ -5672,7 +5771,9 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { ALOGD("Resetting and dropping all events (%s).", reason); } - CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, reason); + ScopedSyntheticEventTracer traceContext(mTracer); + CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, reason, + traceContext.getTracker()); synthesizeCancelationEventsForAllConnectionsLocked(options); resetKeyRepeatLocked(); @@ -5700,7 +5801,7 @@ std::string InputDispatcher::dumpPointerCaptureStateLocked() const { std::string dump; dump += StringPrintf(INDENT "Pointer Capture Requested: %s\n", - toString(mCurrentPointerCaptureRequest.enable)); + toString(mCurrentPointerCaptureRequest.isEnable())); std::string windowName = "None"; if (mWindowTokenWithPointerCapture) { @@ -6067,12 +6168,13 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { return BAD_VALUE; } + ScopedSyntheticEventTracer traceContext(mTracer); for (const DeviceId deviceId : deviceIds) { TouchState& state = *statePtr; TouchedWindow& window = *windowPtr; // Send cancel events to all the input channels we're stealing from. CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, - "input channel stole pointer stream"); + "input channel stole pointer stream", traceContext.getTracker()); options.deviceId = deviceId; options.displayId = displayId; std::vector<PointerProperties> pointers = window.getTouchingPointers(deviceId); @@ -6118,7 +6220,7 @@ void InputDispatcher::requestPointerCapture(const sp<IBinder>& windowToken, bool return; } - if (enabled == mCurrentPointerCaptureRequest.enable) { + if (enabled == mCurrentPointerCaptureRequest.isEnable()) { ALOGW("Ignoring request to %s Pointer Capture: " "window has %s requested pointer capture.", enabled ? "enable" : "disable", enabled ? "already" : "not"); @@ -6134,7 +6236,7 @@ void InputDispatcher::requestPointerCapture(const sp<IBinder>& windowToken, bool } } - setPointerCaptureLocked(enabled); + setPointerCaptureLocked(enabled ? windowToken : nullptr); } // release lock // Wake the thread to process command entries. @@ -6496,7 +6598,8 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, "application handled the original non-fallback key " "or is no longer a foreground target, " - "canceling previously dispatched fallback key"); + "canceling previously dispatched fallback key", + keyEntry.traceTracker); options.keyCode = *fallbackKeyCode; synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection); } @@ -6578,7 +6681,8 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl const auto windowHandle = getWindowHandleLocked(connection->getToken()); if (windowHandle != nullptr) { CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, - "canceling fallback, policy no longer desires it"); + "canceling fallback, policy no longer desires it", + keyEntry.traceTracker); options.keyCode = *fallbackKeyCode; synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection); } @@ -6614,6 +6718,10 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl *fallbackKeyCode, event.getScanCode(), event.getMetaState(), event.getRepeatCount(), event.getDownTime()); + if (mTracer) { + newEntry->traceTracker = + mTracer->traceDerivedEvent(*newEntry, *keyEntry.traceTracker); + } if (DEBUG_OUTBOUND_EVENT_DETAILS) { ALOGD("Unhandled key event: Dispatching fallback key. " "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x", @@ -6712,16 +6820,19 @@ void InputDispatcher::setFocusedWindow(const FocusRequest& request) { std::scoped_lock _l(mLock); std::optional<FocusResolver::FocusChanges> changes = mFocusResolver.setFocusedWindow(request, getWindowHandlesLocked(request.displayId)); + ScopedSyntheticEventTracer traceContext(mTracer); if (changes) { - onFocusChangedLocked(*changes); + onFocusChangedLocked(*changes, traceContext.getTracker()); } } // release lock // Wake up poll loop since it may need to make new input dispatching choices. mLooper->wake(); } -void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes, - const sp<WindowInfoHandle> removedFocusedWindowHandle) { +void InputDispatcher::onFocusChangedLocked( + const FocusResolver::FocusChanges& changes, + const std::unique_ptr<trace::EventTrackerInterface>& traceTracker, + const sp<WindowInfoHandle> removedFocusedWindowHandle) { if (changes.oldFocus) { const auto resolvedWindow = removedFocusedWindowHandle != nullptr ? removedFocusedWindowHandle @@ -6730,7 +6841,7 @@ void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& ch LOG(FATAL) << __func__ << ": Previously focused token did not have a window"; } CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, - "focus left window"); + "focus left window", traceTracker); synthesizeCancelationEventsForWindowLocked(resolvedWindow, options); enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason); } @@ -6755,14 +6866,14 @@ void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& ch } void InputDispatcher::disablePointerCaptureForcedLocked() { - if (!mCurrentPointerCaptureRequest.enable && !mWindowTokenWithPointerCapture) { + if (!mCurrentPointerCaptureRequest.isEnable() && !mWindowTokenWithPointerCapture) { return; } ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus."); - if (mCurrentPointerCaptureRequest.enable) { - setPointerCaptureLocked(false); + if (mCurrentPointerCaptureRequest.isEnable()) { + setPointerCaptureLocked(nullptr); } if (!mWindowTokenWithPointerCapture) { @@ -6782,8 +6893,8 @@ void InputDispatcher::disablePointerCaptureForcedLocked() { mInboundQueue.push_front(std::move(entry)); } -void InputDispatcher::setPointerCaptureLocked(bool enable) { - mCurrentPointerCaptureRequest.enable = enable; +void InputDispatcher::setPointerCaptureLocked(const sp<IBinder>& windowToken) { + mCurrentPointerCaptureRequest.window = windowToken; mCurrentPointerCaptureRequest.seq++; auto command = [this, request = mCurrentPointerCaptureRequest]() REQUIRES(mLock) { scoped_unlock unlock(mLock); @@ -6883,9 +6994,10 @@ void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged( void InputDispatcher::cancelCurrentTouch() { { std::scoped_lock _l(mLock); + ScopedSyntheticEventTracer traceContext(mTracer); ALOGD("Canceling all ongoing pointer gestures on all displays."); CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, - "cancel current touch"); + "cancel current touch", traceContext.getTracker()); synthesizeCancelationEventsForAllConnectionsLocked(options); mTouchStatesByDisplay.clear(); @@ -6935,12 +7047,12 @@ void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFl } } -void InputDispatcher::transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags, - ftl::Flags<InputTarget::Flags> newTargetFlags, - const sp<WindowInfoHandle> fromWindowHandle, - const sp<WindowInfoHandle> toWindowHandle, - TouchState& state, int32_t deviceId, - const std::vector<PointerProperties>& pointers) { +void InputDispatcher::transferWallpaperTouch( + ftl::Flags<InputTarget::Flags> oldTargetFlags, + ftl::Flags<InputTarget::Flags> newTargetFlags, const sp<WindowInfoHandle> fromWindowHandle, + const sp<WindowInfoHandle> toWindowHandle, TouchState& state, int32_t deviceId, + const std::vector<PointerProperties>& pointers, + const std::unique_ptr<trace::EventTrackerInterface>& traceTracker) { const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) && fromWindowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); @@ -6958,7 +7070,7 @@ void InputDispatcher::transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldT if (oldWallpaper != nullptr) { CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, - "transferring touch focus to another window"); + "transferring touch focus to another window", traceTracker); state.removeWindowByToken(oldWallpaper->getToken()); synthesizeCancelationEventsForWindowLocked(oldWallpaper, options); } @@ -6978,7 +7090,7 @@ void InputDispatcher::transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldT getConnectionLocked(toWindowHandle->getToken()); toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState); synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, wallpaperConnection, - wallpaperFlags); + wallpaperFlags, traceTracker); } } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 269bfddb8c..13571b3b5f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -421,7 +421,8 @@ private: void disablePointerCaptureForcedLocked() REQUIRES(mLock); // Set the Pointer Capture state in the Policy. - void setPointerCaptureLocked(bool enable) REQUIRES(mLock); + // The window is not nullptr for requests to enable, otherwise it is nullptr. + void setPointerCaptureLocked(const sp<IBinder>& window) REQUIRES(mLock); // Dispatcher state at time of last ANR. std::string mLastAnrState GUARDED_BY(mLock); @@ -628,7 +629,8 @@ private: void synthesizePointerDownEventsForConnectionLocked( const nsecs_t downTime, const std::shared_ptr<Connection>& connection, - ftl::Flags<InputTarget::Flags> targetFlags) REQUIRES(mLock); + ftl::Flags<InputTarget::Flags> targetFlags, + const std::unique_ptr<trace::EventTrackerInterface>& traceTracker) REQUIRES(mLock); // Splitting motion events across windows. When splitting motion event for a target, // splitDownTime refers to the time of first 'down' event on that particular target @@ -657,6 +659,7 @@ private: void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken, const KeyEntry& entry) REQUIRES(mLock); void onFocusChangedLocked(const FocusResolver::FocusChanges& changes, + const std::unique_ptr<trace::EventTrackerInterface>& traceTracker, const sp<gui::WindowInfoHandle> removedFocusedWindowHandle = nullptr) REQUIRES(mLock); void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) @@ -704,7 +707,9 @@ private: const sp<android::gui::WindowInfoHandle> fromWindowHandle, const sp<android::gui::WindowInfoHandle> toWindowHandle, TouchState& state, int32_t deviceId, - const std::vector<PointerProperties>& pointers) REQUIRES(mLock); + const std::vector<PointerProperties>& pointers, + const std::unique_ptr<trace::EventTrackerInterface>& traceTracker) + REQUIRES(mLock); sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow( const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp index 10f6b0f768..49e6e21828 100644 --- a/services/inputflinger/dispatcher/trace/InputTracer.cpp +++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp @@ -59,6 +59,16 @@ TracedEvent createTracedEvent(const KeyEntry& e) { e.downTime, e.flags, e.repeatCount}; } +void writeEventToBackend(const TracedEvent& event, InputTracingBackendInterface& backend) { + std::visit(Visitor{[&](const TracedMotionEvent& e) { backend.traceMotionEvent(e); }, + [&](const TracedKeyEvent& e) { backend.traceKeyEvent(e); }}, + event); +} + +inline auto getId(const trace::TracedEvent& v) { + return std::visit([](const auto& event) { return event.id; }, v); +} + } // namespace // --- InputTracer --- @@ -67,45 +77,81 @@ InputTracer::InputTracer(std::unique_ptr<InputTracingBackendInterface> backend) : mBackend(std::move(backend)) {} std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const EventEntry& entry) { - TracedEvent traced; + // This is a newly traced inbound event. Create a new state to track it and its derived events. + auto eventState = std::make_shared<EventState>(*this); if (entry.type == EventEntry::Type::MOTION) { const auto& motion = static_cast<const MotionEntry&>(entry); - traced = createTracedEvent(motion); + eventState->events.emplace_back(createTracedEvent(motion)); } else if (entry.type == EventEntry::Type::KEY) { const auto& key = static_cast<const KeyEntry&>(entry); - traced = createTracedEvent(key); + eventState->events.emplace_back(createTracedEvent(key)); } else { LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type); } - return std::make_unique<EventTrackerImpl>(*this, std::move(traced)); + return std::make_unique<EventTrackerImpl>(std::move(eventState), /*isDerived=*/false); +} + +std::unique_ptr<EventTrackerInterface> InputTracer::createTrackerForSyntheticEvent() { + // Create a new EventState to track events derived from this tracker. + return std::make_unique<EventTrackerImpl>(std::make_shared<EventState>(*this), + /*isDerived=*/false); } void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie, const InputTarget& target) { + if (isDerivedCookie(cookie)) { + LOG(FATAL) << "Event target cannot be updated from a derived cookie."; + } auto& eventState = getState(cookie); - if (eventState.isEventProcessingComplete) { - LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()"; + if (eventState->isEventProcessingComplete) { + // TODO(b/210460522): Disallow adding new targets after eventProcessingComplete() is called. + return; } // TODO(b/210460522): Determine if the event is sensitive based on the target. } void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) { + if (isDerivedCookie(cookie)) { + LOG(FATAL) << "Event processing cannot be set from a derived cookie."; + } auto& eventState = getState(cookie); - if (eventState.isEventProcessingComplete) { + if (eventState->isEventProcessingComplete) { LOG(FATAL) << "Traced event was already logged. " "eventProcessingComplete() was likely called more than once."; } + eventState->onEventProcessingComplete(); +} + +std::unique_ptr<EventTrackerInterface> InputTracer::traceDerivedEvent( + const EventEntry& entry, const EventTrackerInterface& originalEventCookie) { + // This is an event derived from an already-established event. Use the same state to track + // this event too. + auto eventState = getState(originalEventCookie); + + if (entry.type == EventEntry::Type::MOTION) { + const auto& motion = static_cast<const MotionEntry&>(entry); + eventState->events.emplace_back(createTracedEvent(motion)); + } else if (entry.type == EventEntry::Type::KEY) { + const auto& key = static_cast<const KeyEntry&>(entry); + eventState->events.emplace_back(createTracedEvent(key)); + } else { + LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type); + } - std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); }, - [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }}, - eventState.event); - eventState.isEventProcessingComplete = true; + if (eventState->isEventProcessingComplete) { + // It is possible for a derived event to be dispatched some time after the original event + // is dispatched, such as in the case of key fallback events. To account for these cases, + // derived events can be traced after the processing is complete for the original event. + writeEventToBackend(eventState->events.back(), *mBackend); + } + return std::make_unique<EventTrackerImpl>(std::move(eventState), /*isDerived=*/true); } void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry, - const EventTrackerInterface* cookie) { + const EventTrackerInterface& cookie) { + auto& eventState = getState(cookie); const EventEntry& entry = *dispatchEntry.eventEntry; // TODO(b/328618922): Remove resolved key repeats after making repeatCount non-mutable. // The KeyEntry's repeatCount is mutable and can be modified after an event is initially traced, @@ -124,44 +170,71 @@ void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry, LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type); } - if (!cookie) { - // This event was not tracked as an inbound event, so trace it now. - std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); }, - [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }}, - traced); + auto tracedEventIt = + std::find_if(eventState->events.begin(), eventState->events.end(), + [&traced](const auto& event) { return getId(traced) == getId(event); }); + if (tracedEventIt == eventState->events.end()) { + LOG(FATAL) + << __func__ + << ": Failed to find a previously traced event that matches the dispatched event"; } // The vsyncId only has meaning if the event is targeting a window. const int32_t windowId = dispatchEntry.windowId.value_or(0); const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0; - mBackend->traceWindowDispatch({std::move(traced), dispatchEntry.deliveryTime, - dispatchEntry.resolvedFlags, dispatchEntry.targetUid, vsyncId, - windowId, dispatchEntry.transform, dispatchEntry.rawTransform, - /*hmac=*/{}, resolvedKeyRepeatCount}); + const WindowDispatchArgs windowDispatchArgs{std::move(traced), + dispatchEntry.deliveryTime, + dispatchEntry.resolvedFlags, + dispatchEntry.targetUid, + vsyncId, + windowId, + dispatchEntry.transform, + dispatchEntry.rawTransform, + /*hmac=*/{}, + resolvedKeyRepeatCount}; + if (eventState->isEventProcessingComplete) { + mBackend->traceWindowDispatch(std::move(windowDispatchArgs)); + } else { + eventState->pendingDispatchArgs.emplace_back(std::move(windowDispatchArgs)); + } } -InputTracer::EventState& InputTracer::getState(const EventTrackerInterface& cookie) { +std::shared_ptr<InputTracer::EventState>& InputTracer::getState( + const EventTrackerInterface& cookie) { return static_cast<const EventTrackerImpl&>(cookie).mState; } -// --- InputTracer::EventTrackerImpl --- +bool InputTracer::isDerivedCookie(const EventTrackerInterface& cookie) { + return static_cast<const EventTrackerImpl&>(cookie).mIsDerived; +} + +// --- InputTracer::EventState --- -InputTracer::EventTrackerImpl::EventTrackerImpl(InputTracer& tracer, TracedEvent&& event) - : mTracer(tracer), mState(event) {} +void InputTracer::EventState::onEventProcessingComplete() { + // Write all of the events known so far to the trace. + for (const auto& event : events) { + writeEventToBackend(event, *tracer.mBackend); + } + // Write all pending dispatch args to the trace. + for (const auto& windowDispatchArgs : pendingDispatchArgs) { + tracer.mBackend->traceWindowDispatch(windowDispatchArgs); + } + pendingDispatchArgs.clear(); + + isEventProcessingComplete = true; +} -InputTracer::EventTrackerImpl::~EventTrackerImpl() { - if (mState.isEventProcessingComplete) { +InputTracer::EventState::~EventState() { + if (isEventProcessingComplete) { // This event has already been written to the trace as expected. return; } // The event processing was never marked as complete, so do it now. - // TODO(b/210460522): Determine why/where the event is being destroyed before - // eventProcessingComplete() is called. - std::visit(Visitor{[&](const TracedMotionEvent& e) { mTracer.mBackend->traceMotionEvent(e); }, - [&](const TracedKeyEvent& e) { mTracer.mBackend->traceKeyEvent(e); }}, - mState.event); - mState.isEventProcessingComplete = true; + // We should never end up here in normal operation. However, in tests, it's possible that we + // stop and destroy InputDispatcher without waiting for it to finish processing events, at + // which point an event (and thus its EventState) may be destroyed before processing finishes. + onEventProcessingComplete(); } } // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h index 1acac6df20..8da9632d7c 100644 --- a/services/inputflinger/dispatcher/trace/InputTracer.h +++ b/services/inputflinger/dispatcher/trace/InputTracer.h @@ -42,38 +42,52 @@ public: InputTracer& operator=(const InputTracer&) = delete; std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) override; + std::unique_ptr<EventTrackerInterface> createTrackerForSyntheticEvent() override; void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) override; void eventProcessingComplete(const EventTrackerInterface&) override; - void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) override; + std::unique_ptr<EventTrackerInterface> traceDerivedEvent(const EventEntry&, + const EventTrackerInterface&) override; + void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface&) override; private: std::unique_ptr<InputTracingBackendInterface> mBackend; - // The state of a tracked event. + using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs; + + // The state of a tracked event, shared across all events derived from the original event. struct EventState { - explicit inline EventState(TracedEvent event) : event(std::move(event)){}; + explicit inline EventState(InputTracer& tracer) : tracer(tracer){}; + ~EventState(); + + void onEventProcessingComplete(); - const TracedEvent event; + InputTracer& tracer; + std::vector<const TracedEvent> events; bool isEventProcessingComplete{false}; + // A queue to hold dispatch args from being traced until event processing is complete. + std::vector<const WindowDispatchArgs> pendingDispatchArgs; // TODO(b/210460522): Add additional args for tracking event sensitivity and // dispatch target UIDs. }; // Get the event state associated with a tracking cookie. - EventState& getState(const EventTrackerInterface&); + std::shared_ptr<EventState>& getState(const EventTrackerInterface&); + bool isDerivedCookie(const EventTrackerInterface&); // Implementation of the event tracker cookie. The cookie holds the event state directly for // convenience to avoid the overhead of tracking the state separately in InputTracer. class EventTrackerImpl : public EventTrackerInterface { public: - explicit EventTrackerImpl(InputTracer&, TracedEvent&& entry); - virtual ~EventTrackerImpl() override; + inline EventTrackerImpl(const std::shared_ptr<EventState>& state, bool isDerivedEvent) + : mState(state), mIsDerived(isDerivedEvent) {} + EventTrackerImpl(const EventTrackerImpl&) = default; private: - InputTracer& mTracer; - mutable EventState mState; + mutable std::shared_ptr<EventState> mState; + const bool mIsDerived; - friend EventState& InputTracer::getState(const EventTrackerInterface&); + friend std::shared_ptr<EventState>& InputTracer::getState(const EventTrackerInterface&); + friend bool InputTracer::isDerivedCookie(const EventTrackerInterface&); }; }; diff --git a/services/inputflinger/dispatcher/trace/InputTracerInterface.h b/services/inputflinger/dispatcher/trace/InputTracerInterface.h index c6cd7de4b6..609d10c0f7 100644 --- a/services/inputflinger/dispatcher/trace/InputTracerInterface.h +++ b/services/inputflinger/dispatcher/trace/InputTracerInterface.h @@ -54,6 +54,14 @@ public: virtual std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) = 0; /** + * Create a trace tracker for a synthetic event that does not stem from an inbound input event. + * This includes things like generating cancellations or down events for various reasons, + * such as ANR, pilfering, transfer touch, etc. Any key or motion events generated for this + * synthetic event should be traced as a derived event using {@link #traceDerivedEvent}. + */ + virtual std::unique_ptr<EventTrackerInterface> createTrackerForSyntheticEvent() = 0; + + /** * Notify the tracer that the traced event will be sent to the given InputTarget. * The tracer may change how the event is logged depending on the target. For example, * events targeting certain UIDs may be logged as sensitive events. @@ -76,12 +84,25 @@ public: virtual void eventProcessingComplete(const EventTrackerInterface&) = 0; /** + * Trace an input event that is derived from another event. This is used in cases where an event + * is modified from the original, such as when a touch is split across multiple windows, or + * when a HOVER_MOVE event is modified to be a HOVER_EXIT, etc. The original event's tracker + * must be provided, and a new EventTracker is returned that should be used to track the event's + * lifecycle. + * + * NOTE: The derived tracker cannot be used to change the targets of the original event, meaning + * it cannot be used with {@link #dispatchToTargetHint} or {@link eventProcessingComplete}. + */ + virtual std::unique_ptr<EventTrackerInterface> traceDerivedEvent( + const EventEntry&, const EventTrackerInterface& originalEventTracker) = 0; + + /** * Trace an input event being successfully dispatched to a window. The dispatched event may - * be a previously traced inbound event, or it may be a synthesized event that has not been - * previously traced. For inbound events that were previously traced, the EventTracker cookie - * must be provided. For events that were not previously traced, the cookie must be null. + * be a previously traced inbound event, or it may be a synthesized event. All dispatched events + * must have been previously traced, so the trace tracker associated with the event must be + * provided. */ - virtual void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) = 0; + virtual void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface&) = 0; }; } // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/include/NotifyArgsBuilders.h b/services/inputflinger/include/NotifyArgsBuilders.h index e4363a416c..8ffbc11a13 100644 --- a/services/inputflinger/include/NotifyArgsBuilders.h +++ b/services/inputflinger/include/NotifyArgsBuilders.h @@ -107,7 +107,9 @@ public: // Set mouse cursor position for the most common cases to avoid boilerplate. if (mSource == AINPUT_SOURCE_MOUSE && - !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) { + !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) && + BitSet64::hasBit(pointerCoords[0].bits, AMOTION_EVENT_AXIS_X) && + BitSet64::hasBit(pointerCoords[0].bits, AMOTION_EVENT_AXIS_Y)) { mRawXCursorPosition = pointerCoords[0].getX(); mRawYCursorPosition = pointerCoords[0].getY(); } diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 06f10e5445..c8cc5dcc83 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -486,7 +486,7 @@ void CursorInputMapper::configureBasicParams() { } void CursorInputMapper::configureOnPointerCapture(const InputReaderConfiguration& config) { - if (config.pointerCaptureRequest.enable) { + if (config.pointerCaptureRequest.isEnable()) { if (mParameters.mode == Parameters::Mode::POINTER) { mParameters.mode = Parameters::Mode::POINTER_RELATIVE; mSource = AINPUT_SOURCE_MOUSE_RELATIVE; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 3c26d1d73e..7d27d4a9ce 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -923,7 +923,7 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) // Determine device mode. if (mParameters.deviceType == Parameters::DeviceType::POINTER && - mConfig.pointerGesturesEnabled && !mConfig.pointerCaptureRequest.enable) { + mConfig.pointerGesturesEnabled && !mConfig.pointerCaptureRequest.isEnable()) { mSource = AINPUT_SOURCE_MOUSE; mDeviceMode = DeviceMode::POINTER; if (hasStylus()) { @@ -1038,7 +1038,7 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) (mDeviceMode == DeviceMode::POINTER) || // - when pointer capture is enabled, to preserve the mouse cursor position; (mParameters.deviceType == Parameters::DeviceType::POINTER && - mConfig.pointerCaptureRequest.enable) || + mConfig.pointerCaptureRequest.isEnable()) || // - when we should be showing touches; (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) || // - when we should be showing a pointer icon for direct styluses. @@ -1047,7 +1047,7 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) if (mPointerController == nullptr) { mPointerController = getContext()->getPointerController(getDeviceId()); } - if (mConfig.pointerCaptureRequest.enable) { + if (mConfig.pointerCaptureRequest.isEnable()) { mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); } } else { diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index eacc66eeab..99f9e24169 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -410,9 +410,9 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, .setBoolValues({config.touchpadRightClickZoneEnabled}); } std::list<NotifyArgs> out; - if ((!changes.any() && config.pointerCaptureRequest.enable) || + if ((!changes.any() && config.pointerCaptureRequest.isEnable()) || changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE)) { - mPointerCaptured = config.pointerCaptureRequest.enable; + mPointerCaptured = config.pointerCaptureRequest.isEnable(); // The motion ranges are going to change, so bump the generation to clear the cached ones. bumpGeneration(); if (mPointerCaptured) { diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index a26153ec5b..09ae6dd28c 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -39,7 +39,6 @@ cc_test { ], srcs: [ "AnrTracker_test.cpp", - "BlockingQueue_test.cpp", "CapturedTouchpadEventConverter_test.cpp", "CursorInputMapper_test.cpp", "EventHub_test.cpp", diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp index de740672fc..c44f88006e 100644 --- a/services/inputflinger/tests/CursorInputMapper_test.cpp +++ b/services/inputflinger/tests/CursorInputMapper_test.cpp @@ -166,7 +166,7 @@ protected: } void setPointerCapture(bool enabled) { - mReaderConfiguration.pointerCaptureRequest.enable = enabled; + mReaderConfiguration.pointerCaptureRequest.window = enabled ? sp<BBinder>::make() : nullptr; mReaderConfiguration.pointerCaptureRequest.seq = 1; int32_t generation = mDevice->getGeneration(); std::list<NotifyArgs> args = diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index 9e9371248e..8f593b553a 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -178,8 +178,8 @@ void FakeInputReaderPolicy::setTouchAffineTransformation(const TouchAffineTransf transform = t; } -PointerCaptureRequest FakeInputReaderPolicy::setPointerCapture(bool enabled) { - mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++}; +PointerCaptureRequest FakeInputReaderPolicy::setPointerCapture(const sp<IBinder>& window) { + mConfig.pointerCaptureRequest = {window, mNextPointerCaptureSequenceNumber++}; return mConfig.pointerCaptureRequest; } diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index da5085db7c..710bb5496f 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -68,7 +68,7 @@ public: TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation); void setTouchAffineTransformation(const TouchAffineTransformation t); - PointerCaptureRequest setPointerCapture(bool enabled); + PointerCaptureRequest setPointerCapture(const sp<IBinder>& window); void setShowTouches(bool enabled); void setDefaultPointerDisplayId(int32_t pointerDisplayId); void setPointerGestureEnabled(bool enabled); diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp index 2ff9c3c784..cb8c3cb03c 100644 --- a/services/inputflinger/tests/FocusResolver_test.cpp +++ b/services/inputflinger/tests/FocusResolver_test.cpp @@ -20,6 +20,7 @@ #define ASSERT_FOCUS_CHANGE(_changes, _oldFocus, _newFocus) \ { \ + ASSERT_TRUE(_changes.has_value()); \ ASSERT_EQ(_oldFocus, _changes->oldFocus); \ ASSERT_EQ(_newFocus, _changes->newFocus); \ } @@ -152,6 +153,38 @@ TEST(FocusResolverTest, SetFocusedMirroredWindow) { ASSERT_FOCUS_CHANGE(changes, /*from*/ invisibleWindowToken, /*to*/ nullptr); } +TEST(FocusResolverTest, FocusTransferToMirror) { + sp<IBinder> focusableWindowToken = sp<BBinder>::make(); + auto window = sp<FakeWindowHandle>::make("Window", focusableWindowToken, + /*focusable=*/true, /*visible=*/true); + auto mirror = sp<FakeWindowHandle>::make("Mirror", focusableWindowToken, + /*focusable=*/true, /*visible=*/true); + + FocusRequest request; + request.displayId = 42; + request.token = focusableWindowToken; + FocusResolver focusResolver; + std::optional<FocusResolver::FocusChanges> changes = + focusResolver.setFocusedWindow(request, {window, mirror}); + ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken); + + // The mirror window now comes on top, and the focus does not change + changes = focusResolver.setInputWindows(request.displayId, {mirror, window}); + ASSERT_FALSE(changes.has_value()); + + // The window now comes on top while the mirror is removed, and the focus does not change + changes = focusResolver.setInputWindows(request.displayId, {window}); + ASSERT_FALSE(changes.has_value()); + + // The window is removed but the mirror is on top, and focus does not change + changes = focusResolver.setInputWindows(request.displayId, {mirror}); + ASSERT_FALSE(changes.has_value()); + + // All windows removed + changes = focusResolver.setInputWindows(request.displayId, {}); + ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr); +} + TEST(FocusResolverTest, SetInputWindows) { sp<IBinder> focusableWindowToken = sp<BBinder>::make(); std::vector<sp<WindowInfoHandle>> windows; @@ -169,6 +202,10 @@ TEST(FocusResolverTest, SetInputWindows) { focusResolver.setFocusedWindow(request, windows); ASSERT_EQ(focusableWindowToken, changes->newFocus); + // When there are no changes to the window, focus does not change + changes = focusResolver.setInputWindows(request.displayId, windows); + ASSERT_FALSE(changes.has_value()); + // Window visibility changes and the window loses focus window->setVisible(false); changes = focusResolver.setInputWindows(request.displayId, windows); @@ -380,18 +417,13 @@ TEST(FocusResolverTest, FocusRequestsAreClearedWhenWindowIsRemoved) { ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken); ASSERT_EQ(request.displayId, changes->displayId); - // Start with a focused window - window->setFocusable(true); - changes = focusResolver.setInputWindows(request.displayId, windows); - ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken); - // When a display is removed, all windows are removed from the display // and our focused window loses focus changes = focusResolver.setInputWindows(request.displayId, {}); ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr); focusResolver.displayRemoved(request.displayId); - // When a display is readded, the window does not get focus since the request was cleared. + // When a display is re-added, the window does not get focus since the request was cleared. changes = focusResolver.setInputWindows(request.displayId, windows); ASSERT_FALSE(changes); } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f0f4d93ecd..b3b2ee2c9d 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -15,7 +15,6 @@ */ #include "../dispatcher/InputDispatcher.h" -#include "../BlockingQueue.h" #include "FakeApplicationHandle.h" #include "FakeInputTracingBackend.h" #include "TestEventMatchers.h" @@ -32,6 +31,7 @@ #include <flag_macros.h> #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <input/BlockingQueue.h> #include <input/Input.h> #include <input/PrintTools.h> #include <linux/input.h> @@ -308,17 +308,22 @@ public: "signal"; } - PointerCaptureRequest assertSetPointerCaptureCalled(bool enabled) { + PointerCaptureRequest assertSetPointerCaptureCalled(const sp<WindowInfoHandle>& window, + bool enabled) { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); - if (!mPointerCaptureChangedCondition.wait_for(lock, 100ms, - [this, enabled]() REQUIRES(mLock) { - return mPointerCaptureRequest->enable == - enabled; - })) { - ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << enabled - << ") to be called."; + if (!mPointerCaptureChangedCondition + .wait_for(lock, 100ms, [this, enabled, window]() REQUIRES(mLock) { + if (enabled) { + return mPointerCaptureRequest->isEnable() && + mPointerCaptureRequest->window == window->getToken(); + } else { + return !mPointerCaptureRequest->isEnable(); + } + })) { + ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << window->getName() << ", " + << enabled << ") to be called."; return {}; } auto request = *mPointerCaptureRequest; @@ -333,7 +338,7 @@ public: if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) { FAIL() << "Expected setPointerCapture(request) to not be called, but was called. " "enabled = " - << std::to_string(mPointerCaptureRequest->enable); + << std::to_string(mPointerCaptureRequest->isEnable()); } mPointerCaptureRequest.reset(); } @@ -7477,6 +7482,12 @@ protected: mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT, /*expectedFlags=*/0); } + + void injectKeyRepeat(int32_t repeatCount) { + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, repeatCount, ADISPLAY_ID_DEFAULT)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + } }; TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeat) { @@ -7565,6 +7576,17 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEvent } } +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_CorrectRepeatCountWhenInjectKeyRepeat) { + injectKeyRepeat(0); + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); + for (int32_t repeatCount = 1; repeatCount <= 2; ++repeatCount) { + expectKeyRepeatOnce(repeatCount); + } + injectKeyRepeat(1); + // Expect repeatCount to be 3 instead of 1 + expectKeyRepeatOnce(3); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: @@ -8709,9 +8731,10 @@ TEST_F(InputDispatcherSingleWindowAnr, StaleKeyEventDoesNotAnr) { // Define a valid key down event that is stale (too old). event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /*flags=*/0, AKEYCODE_A, KEY_A, - AMETA_NONE, /*repeatCount=*/1, eventTime, eventTime); + AMETA_NONE, /*repeatCount=*/0, eventTime, eventTime); - const int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER; + const int32_t policyFlags = + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT; InputEventInjectionResult result = mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, @@ -9870,7 +9893,7 @@ protected: PointerCaptureRequest requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window, bool enabled) { mDispatcher->requestPointerCapture(window->getToken(), enabled); - auto request = mFakePolicy->assertSetPointerCaptureCalled(enabled); + auto request = mFakePolicy->assertSetPointerCaptureCalled(window, enabled); notifyPointerCaptureChanged(request); window->consumeCaptureEvent(enabled); return request; @@ -9903,7 +9926,7 @@ TEST_F(InputDispatcherPointerCaptureTests, DisablesPointerCaptureAfterWindowLose mWindow->consumeCaptureEvent(false); mWindow->consumeFocusEvent(false); mSecondWindow->consumeFocusEvent(true); - mFakePolicy->assertSetPointerCaptureCalled(false); + mFakePolicy->assertSetPointerCaptureCalled(mWindow, false); // Ensure that additional state changes from InputReader are not sent to the window. notifyPointerCaptureChanged({}); @@ -9922,7 +9945,7 @@ TEST_F(InputDispatcherPointerCaptureTests, UnexpectedStateChangeDisablesPointerC notifyPointerCaptureChanged(request); // Ensure that Pointer Capture is disabled. - mFakePolicy->assertSetPointerCaptureCalled(false); + mFakePolicy->assertSetPointerCaptureCalled(mWindow, false); mWindow->consumeCaptureEvent(false); mWindow->assertNoEvents(); } @@ -9932,13 +9955,13 @@ TEST_F(InputDispatcherPointerCaptureTests, OutOfOrderRequests) { // The first window loses focus. setFocusedWindow(mSecondWindow); - mFakePolicy->assertSetPointerCaptureCalled(false); + mFakePolicy->assertSetPointerCaptureCalled(mWindow, false); mWindow->consumeCaptureEvent(false); // Request Pointer Capture from the second window before the notification from InputReader // arrives. mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true); - auto request = mFakePolicy->assertSetPointerCaptureCalled(true); + auto request = mFakePolicy->assertSetPointerCaptureCalled(mSecondWindow, true); // InputReader notifies Pointer Capture was disabled (because of the focus change). notifyPointerCaptureChanged({}); @@ -9953,11 +9976,11 @@ TEST_F(InputDispatcherPointerCaptureTests, OutOfOrderRequests) { TEST_F(InputDispatcherPointerCaptureTests, EnableRequestFollowsSequenceNumbers) { // App repeatedly enables and disables capture. mDispatcher->requestPointerCapture(mWindow->getToken(), true); - auto firstRequest = mFakePolicy->assertSetPointerCaptureCalled(true); + auto firstRequest = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true); mDispatcher->requestPointerCapture(mWindow->getToken(), false); - mFakePolicy->assertSetPointerCaptureCalled(false); + mFakePolicy->assertSetPointerCaptureCalled(mWindow, false); mDispatcher->requestPointerCapture(mWindow->getToken(), true); - auto secondRequest = mFakePolicy->assertSetPointerCaptureCalled(true); + auto secondRequest = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true); // InputReader notifies that PointerCapture has been enabled for the first request. Since the // first request is now stale, this should do nothing. @@ -9974,10 +9997,10 @@ TEST_F(InputDispatcherPointerCaptureTests, RapidToggleRequests) { // App toggles pointer capture off and on. mDispatcher->requestPointerCapture(mWindow->getToken(), false); - mFakePolicy->assertSetPointerCaptureCalled(false); + mFakePolicy->assertSetPointerCaptureCalled(mWindow, false); mDispatcher->requestPointerCapture(mWindow->getToken(), true); - auto enableRequest = mFakePolicy->assertSetPointerCaptureCalled(true); + auto enableRequest = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true); // InputReader notifies that the latest "enable" request was processed, while skipping over the // preceding "disable" request. @@ -10029,6 +10052,26 @@ TEST_F(InputDispatcherPointerCaptureTests, MouseHoverAndPointerCapture) { mWindow->assertNoEvents(); } +using InputDispatcherPointerCaptureDeathTest = InputDispatcherPointerCaptureTests; + +TEST_F(InputDispatcherPointerCaptureDeathTest, + NotifyPointerCaptureChangedWithWrongTokenAbortsDispatcher) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + ScopedSilentDeath _silentDeath; + + mDispatcher->requestPointerCapture(mWindow->getToken(), true); + auto request = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true); + + // Dispatch a pointer changed event with a wrong token. + request.window = mSecondWindow->getToken(); + ASSERT_DEATH( + { + notifyPointerCaptureChanged(request); + mSecondWindow->consumeCaptureEvent(true); + }, + "Unexpected requested window for Pointer Capture."); +} + class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest { protected: constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 835f8b89c3..1d46c9a1e2 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1165,18 +1165,18 @@ TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToSubdeviceMappers) { TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) { NotifyPointerCaptureChangedArgs args; - auto request = mFakePolicy->setPointerCapture(true); + auto request = mFakePolicy->setPointerCapture(/*window=*/sp<BBinder>::make()); mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE); mReader->loopOnce(); mFakeListener->assertNotifyCaptureWasCalled(&args); - ASSERT_TRUE(args.request.enable) << "Pointer Capture should be enabled."; + ASSERT_TRUE(args.request.isEnable()) << "Pointer Capture should be enabled."; ASSERT_EQ(args.request, request) << "Pointer Capture sequence number should match."; - mFakePolicy->setPointerCapture(false); + mFakePolicy->setPointerCapture(/*window=*/nullptr); mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE); mReader->loopOnce(); mFakeListener->assertNotifyCaptureWasCalled(&args); - ASSERT_FALSE(args.request.enable) << "Pointer Capture should be disabled."; + ASSERT_FALSE(args.request.isEnable()) << "Pointer Capture should be disabled."; // Verify that the Pointer Capture state is not updated when the configuration value // does not change. @@ -9802,7 +9802,7 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) { prepareAxes(POSITION | ID | SLOT); mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0); mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0); - mFakePolicy->setPointerCapture(true); + mFakePolicy->setPointerCapture(/*window=*/sp<BBinder>::make()); mFakePolicy->setPointerController(fakePointerController); MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>(); @@ -9934,7 +9934,7 @@ TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) { ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); // non captured touchpad should be a mouse source - mFakePolicy->setPointerCapture(false); + mFakePolicy->setPointerCapture(/*window=*/nullptr); configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); @@ -10012,14 +10012,14 @@ TEST_F(MultiTouchInputMapperTest, WhenCapturedAndNotCaptured_GetSources) { prepareAxes(POSITION | ID | SLOT); mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0); mFakePolicy->setPointerController(fakePointerController); - mFakePolicy->setPointerCapture(false); + mFakePolicy->setPointerCapture(/*window=*/nullptr); MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>(); // uncaptured touchpad should be a pointer device ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); // captured touchpad should be a touchpad device - mFakePolicy->setPointerCapture(true); + mFakePolicy->setPointerCapture(/*window=*/sp<BBinder>::make()); configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE); ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources()); } @@ -10090,7 +10090,7 @@ protected: prepareAbsoluteAxisResolution(xAxisResolution, yAxisResolution); // In order to enable swipe and freeform gesture in pointer mode, pointer capture // needs to be disabled, and the pointer gesture needs to be enabled. - mFakePolicy->setPointerCapture(false); + mFakePolicy->setPointerCapture(/*window=*/nullptr); mFakePolicy->setPointerGestureEnabled(true); mFakePolicy->setPointerController(fakePointerController); diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index e9e50619ee..8ddb672cfe 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -355,6 +355,38 @@ TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) { AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220))); } +TEST_F(PointerChoreographerTest, AbsoluteMouseMovesPointerAndReturnsNewArgs) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); + + // Set initial position of the PointerController. + pc->setPosition(100, 200); + const auto absoluteMousePointer = PointerBuilder(/*id=*/0, ToolType::MOUSE) + .axis(AMOTION_EVENT_AXIS_X, 110) + .axis(AMOTION_EVENT_AXIS_Y, 220); + + // Make NotifyMotionArgs and notify Choreographer. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(absoluteMousePointer) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + + // Check that the PointerController updated the position and the pointer is shown. + pc->assertPosition(110, 220); + ASSERT_TRUE(pc->isPointerShown()); + + // Check that x-y coordinates, displayId and cursor position are correctly updated. + mTestListener.assertNotifyMotionWasCalled( + AllOf(WithCoords(110, 220), WithRelativeMotion(10, 20), WithDisplayId(DISPLAY_ID), + WithCursorPosition(110, 220))); +} + TEST_F(PointerChoreographerTest, AssociatedMouseMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) { // Add two displays and set one to default. @@ -413,7 +445,8 @@ TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) { {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE_RELATIVE, ADISPLAY_ID_NONE)}}); mChoreographer.notifyPointerCaptureChanged( NotifyPointerCaptureChangedArgs(/*id=*/2, systemTime(SYSTEM_TIME_MONOTONIC), - PointerCaptureRequest(/*enable=*/true, /*seq=*/0))); + PointerCaptureRequest(/*window=*/sp<BBinder>::make(), + /*seq=*/0))); // Notify motion as if pointer capture is enabled. mChoreographer.notifyMotion( @@ -450,7 +483,8 @@ TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) { // Enable pointer capture and check if the PointerController hid the pointer. mChoreographer.notifyPointerCaptureChanged( NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC), - PointerCaptureRequest(/*enable=*/true, /*seq=*/0))); + PointerCaptureRequest(/*window=*/sp<BBinder>::make(), + /*seq=*/0))); ASSERT_FALSE(pc->isPointerShown()); } @@ -1295,7 +1329,8 @@ TEST_F(PointerChoreographerTest, DoesNotMovePointerForTouchpadSource) { // Assume that pointer capture is enabled. mChoreographer.notifyPointerCaptureChanged( NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC), - PointerCaptureRequest(/*enable=*/true, /*seq=*/0))); + PointerCaptureRequest(/*window=*/sp<BBinder>::make(), + /*seq=*/0))); // Notify motion as if pointer capture is enabled. mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHPAD) @@ -1329,7 +1364,8 @@ TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledTouchpadHidesPointer) // Enable pointer capture and check if the PointerController hid the pointer. mChoreographer.notifyPointerCaptureChanged( NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC), - PointerCaptureRequest(/*enable=*/true, /*seq=*/0))); + PointerCaptureRequest(/*window=*/sp<BBinder>::make(), + /*seq=*/0))); ASSERT_FALSE(pc->isPointerShown()); } diff --git a/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp index 219b662ffb..863d0a165e 100644 --- a/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp @@ -15,8 +15,8 @@ */ #include <fuzzer/FuzzedDataProvider.h> +#include <input/BlockingQueue.h> #include <thread> -#include "BlockingQueue.h" // Chosen to be a number large enough for variation in fuzzer runs, but not consume too much memory. static constexpr size_t MAX_CAPACITY = 1024; diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp index c2bf275611..643e8b9f97 100644 --- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp @@ -125,6 +125,9 @@ void setTouchpadSettings(ThreadSafeFuzzedDataProvider& fdp, InputReaderConfigura config.touchpadTapToClickEnabled = fdp.ConsumeBool(); config.touchpadTapDraggingEnabled = fdp.ConsumeBool(); config.touchpadRightClickZoneEnabled = fdp.ConsumeBool(); + + config.pointerCaptureRequest.window = fdp.ConsumeBool() ? sp<BBinder>::make() : nullptr; + config.pointerCaptureRequest.seq = fdp.ConsumeIntegral<uint32_t>(); } } // namespace @@ -145,7 +148,6 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { // Some settings are fuzzed here, as well as in the main loop, to provide randomized data to the // TouchpadInputMapper constructor. setTouchpadSettings(*fdp, policyConfig); - policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool(); TouchpadInputMapper& mapper = getMapperForDevice<ThreadSafeFuzzedDataProvider, TouchpadInputMapper>(*fdp, device, policyConfig); @@ -164,7 +166,6 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { [&]() -> void { mapper.getSources(); }, [&]() -> void { setTouchpadSettings(*fdp, policyConfig); - policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool(); std::list<NotifyArgs> unused = mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig, InputReaderConfiguration::Change( diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index d77180362b..dc69b819c8 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -83,11 +83,11 @@ cc_defaults { "libprotobuf-cpp-lite", "libsync", "libui", - "libinput", "libutils", "libSurfaceFlingerProp", ], static_libs: [ + "iinputflinger_aidl_lib_static", "libaidlcommonsupport", "libcompositionengine", "libframetimeline", @@ -210,7 +210,6 @@ filegroup { "Scheduler/VsyncModulator.cpp", "Scheduler/VsyncSchedule.cpp", "ScreenCaptureOutput.cpp", - "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", "SurfaceFlingerDefaultFactory.cpp", "Tracing/LayerDataSource.cpp", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h index 18a96f4de7..843b5c5c82 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h @@ -19,6 +19,7 @@ #include <chrono> #include <optional> #include <vector> +#include "utils/Timers.h" #include <compositionengine/Display.h> #include <compositionengine/LayerFE.h> @@ -105,6 +106,9 @@ struct CompositionRefreshArgs { bool hasTrustedPresentationListener = false; ICEPowerCallback* powerCallback = nullptr; + + // System time for when frame refresh starts. Used for stats. + nsecs_t refreshStartTime = 0; }; } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index a1d61323a8..a499928dd0 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -58,8 +58,7 @@ public: // Called before composition starts. Should return true if this layer has // pending updates which would require an extra display refresh cycle to // process. - virtual bool onPreComposition(nsecs_t refreshStartTime, - bool updatingOutputGeometryThisFrame) = 0; + virtual bool onPreComposition(bool updatingOutputGeometryThisFrame) = 0; struct ClientCompositionTargetSettings { enum class BlurSetting { diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h index 15e4577ae0..1b8cc2758f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -43,7 +43,7 @@ public: MOCK_CONST_METHOD0(getCompositionState, const LayerFECompositionState*()); - MOCK_METHOD2(onPreComposition, bool(nsecs_t, bool)); + MOCK_METHOD1(onPreComposition, bool(bool)); MOCK_CONST_METHOD1(prepareClientComposition, std::optional<compositionengine::LayerFE::LayerSettings>( diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp index d87eae3812..b4702085b6 100644 --- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp @@ -181,10 +181,10 @@ void CompositionEngine::preComposition(CompositionRefreshArgs& args) { bool needsAnotherUpdate = false; - mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + mRefreshStartTime = args.refreshStartTime; for (auto& layer : args.layers) { - if (layer->onPreComposition(mRefreshStartTime, args.updatingOutputGeometryThisFrame)) { + if (layer->onPreComposition(args.updatingOutputGeometryThisFrame)) { needsAnotherUpdate = true; } } diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp index da578e2046..042010edd5 100644 --- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp @@ -214,6 +214,7 @@ struct CompositionTestPreComposition : public CompositionEngineTest { TEST_F(CompositionTestPreComposition, preCompositionSetsFrameTimestamp) { const nsecs_t before = systemTime(SYSTEM_TIME_MONOTONIC); + mRefreshArgs.refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC); mEngine.preComposition(mRefreshArgs); const nsecs_t after = systemTime(SYSTEM_TIME_MONOTONIC); @@ -226,12 +227,9 @@ TEST_F(CompositionTestPreComposition, preCompositionInvokesLayerPreCompositionWi nsecs_t ts1 = 0; nsecs_t ts2 = 0; nsecs_t ts3 = 0; - EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)) - .WillOnce(DoAll(SaveArg<0>(&ts1), Return(false))); - EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)) - .WillOnce(DoAll(SaveArg<0>(&ts2), Return(false))); - EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)) - .WillOnce(DoAll(SaveArg<0>(&ts3), Return(false))); + EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts1), Return(false))); + EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false))); + EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false))); mRefreshArgs.outputs = {mOutput1}; mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE}; @@ -245,9 +243,9 @@ TEST_F(CompositionTestPreComposition, preCompositionInvokesLayerPreCompositionWi } TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) { - EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(false)); - EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false)); - EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false)); mEngine.setNeedsAnotherUpdateForTest(true); @@ -262,9 +260,9 @@ TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) { TEST_F(CompositionTestPreComposition, preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) { - EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(true)); - EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false)); - EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true)); + EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false)); mRefreshArgs.outputs = {mOutput1}; mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE}; diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index 8dfbeb80cd..faa51972e9 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -48,6 +48,7 @@ namespace impl { using aidl::android::hardware::power::Boost; using aidl::android::hardware::power::Mode; using aidl::android::hardware::power::SessionHint; +using aidl::android::hardware::power::SessionTag; using aidl::android::hardware::power::WorkDuration; PowerAdvisor::~PowerAdvisor() = default; @@ -204,13 +205,36 @@ bool PowerAdvisor::supportsPowerHintSession() { return *mSupportsHintSession; } +bool PowerAdvisor::shouldCreateSessionWithConfig() { + return mSessionConfigSupported && FlagManager::getInstance().adpf_use_fmq_channel(); +} + bool PowerAdvisor::ensurePowerHintSessionRunning() { if (mHintSession == nullptr && !mHintSessionThreadIds.empty() && usePowerHintSession()) { - auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()), - mHintSessionThreadIds, mTargetDuration.ns()); - - if (ret.isOk()) { - mHintSession = ret.value(); + if (shouldCreateSessionWithConfig()) { + auto ret = getPowerHal().createHintSessionWithConfig(getpid(), + static_cast<int32_t>(getuid()), + mHintSessionThreadIds, + mTargetDuration.ns(), + SessionTag::SURFACEFLINGER, + &mSessionConfig); + if (ret.isOk()) { + mHintSession = ret.value(); + } + // If it fails the first time we try, or ever returns unsupported, assume unsupported + else if (mFirstConfigSupportCheck || ret.isUnsupported()) { + ALOGI("Hint session with config is unsupported, falling back to a legacy session"); + mSessionConfigSupported = false; + } + mFirstConfigSupportCheck = false; + } + // Immediately try original method after, in case the first way returned unsupported + if (mHintSession == nullptr && !shouldCreateSessionWithConfig()) { + auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()), + mHintSessionThreadIds, mTargetDuration.ns()); + if (ret.isOk()) { + mHintSession = ret.value(); + } } } return mHintSession != nullptr; diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 1040048b13..13e1263369 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -230,6 +230,9 @@ private: // this normalizes them together and takes the max of the two Duration combineTimingEstimates(Duration totalDuration, Duration flingerDuration); + // Whether to use the new "createHintSessionWithConfig" method + bool shouldCreateSessionWithConfig() REQUIRES(mHintSessionMutex); + bool ensurePowerHintSessionRunning() REQUIRES(mHintSessionMutex); std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData; @@ -278,6 +281,13 @@ private: std::promise<bool> mDelayReportActualMutexAcquisitonPromise; bool mTimingTestingMode = false; + // Hint session configuration data + aidl::android::hardware::power::SessionConfig mSessionConfig; + + // Whether createHintSessionWithConfig is supported, assume true until it fails + bool mSessionConfigSupported = true; + bool mFirstConfigSupportCheck = true; + // Whether we should emit ATRACE_INT data for hint sessions static const bool sTraceHintSessionData; diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index cb0e2a1938..867f3af4b1 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -163,7 +163,9 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges); if (clientState.what & layer_state_t::eFlagsChanged) { - if ((oldFlags ^ flags) & (layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque)) { + if ((oldFlags ^ flags) & + (layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque | + layer_state_t::eLayerSecure)) { changes |= RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::VisibleRegion; } diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp index 2dbcb841ac..43a4397899 100644 --- a/services/surfaceflinger/LayerFE.cpp +++ b/services/surfaceflinger/LayerFE.cpp @@ -82,8 +82,7 @@ const compositionengine::LayerFECompositionState* LayerFE::getCompositionState() return mSnapshot.get(); } -bool LayerFE::onPreComposition(nsecs_t refreshStartTime, bool) { - mCompositionResult.refreshStartTime = refreshStartTime; +bool LayerFE::onPreComposition(bool) { return mSnapshot->hasReadyFrame; } diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h index d584fb7eab..66cb88b20e 100644 --- a/services/surfaceflinger/LayerFE.h +++ b/services/surfaceflinger/LayerFE.h @@ -26,9 +26,6 @@ namespace android { struct CompositionResult { - // TODO(b/238781169) update CE to no longer pass refreshStartTime to LayerFE::onPreComposition - // and remove this field. - nsecs_t refreshStartTime = 0; std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences; sp<Fence> lastClientCompositionFence = nullptr; }; @@ -39,7 +36,7 @@ public: // compositionengine::LayerFE overrides const compositionengine::LayerFECompositionState* getCompositionState() const override; - bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame) override; + bool onPreComposition(bool updatingOutputGeometryThisFrame) override; void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack) override; const char* getDebugName() const override; int32_t getSequence() const override; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 3f9168252b..d92edb81e0 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -565,7 +565,7 @@ void Scheduler::onHardwareVsyncRequest(PhysicalDisplayId id, bool enabled) { })); } -void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) { +void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate, bool applyImmediately) { std::scoped_lock lock(mDisplayLock); ftl::FakeGuard guard(kMainThreadContext); @@ -586,7 +586,7 @@ void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) { ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(), to_string(mode.modePtr->getVsyncRate()).c_str()); - display.schedulePtr->getTracker().setRenderRate(renderFrameRate); + display.schedulePtr->getTracker().setRenderRate(renderFrameRate, applyImmediately); } Fps Scheduler::getNextFrameInterval(PhysicalDisplayId id, diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 09f75fdaca..494a91bf21 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -188,7 +188,7 @@ public: const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; } // Sets the render rate for the scheduler to run at. - void setRenderRate(PhysicalDisplayId, Fps); + void setRenderRate(PhysicalDisplayId, Fps, bool applyImmediately); void enableHardwareVsync(PhysicalDisplayId) REQUIRES(kMainThreadContext); void disableHardwareVsync(PhysicalDisplayId, bool disallow) REQUIRES(kMainThreadContext); diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index 84ccf8e938..6d6b70d198 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -20,7 +20,7 @@ #include <android-base/stringprintf.h> #include <ftl/concat.h> -#include <utils/Trace.h> +#include <gui/TraceUtils.h> #include <log/log_main.h> #include <scheduler/TimeKeeper.h> @@ -44,6 +44,17 @@ ScheduleResult getExpectedCallbackTime(nsecs_t nextVsyncTime, TimePoint::fromNs(nextVsyncTime)}; } +void traceEntry(const VSyncDispatchTimerQueueEntry& entry, nsecs_t now) { + if (!ATRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) { + return; + } + + ftl::Concat trace(ftl::truncated<5>(entry.name()), " alarm in ", + ns2us(*entry.wakeupTime() - now), "us; VSYNC in ", + ns2us(*entry.targetVsync() - now), "us"); + ATRACE_FORMAT_INSTANT(trace.c_str()); +} + } // namespace VSyncDispatch::~VSyncDispatch() = default; @@ -87,6 +98,7 @@ std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const { ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker, nsecs_t now) { + ATRACE_NAME("VSyncDispatchTimerQueueEntry::schedule"); auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync, now + timing.workDuration + @@ -98,6 +110,8 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance)); bool const wouldSkipAWakeup = mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance))); + ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(), + wouldSkipAVsyncTarget, wouldSkipAWakeup); if (FlagManager::getInstance().dont_skip_on_early_ro()) { if (wouldSkipAVsyncTarget || wouldSkipAWakeup) { nextVsyncTime = mArmedInfo->mActualVsyncTime; @@ -122,7 +136,7 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim ScheduleResult VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate( VSyncTracker& tracker, nsecs_t now, VSyncDispatch::ScheduleTiming timing) { mWorkloadUpdateInfo = timing; - const auto armedInfo = update(tracker, now, timing, mArmedInfo); + const auto armedInfo = getArmedInfo(tracker, now, timing, mArmedInfo); return {TimePoint::fromNs(armedInfo.mActualWakeupTime), TimePoint::fromNs(armedInfo.mActualVsyncTime)}; } @@ -140,11 +154,13 @@ nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker, bool const nextVsyncTooClose = mLastDispatchTime && (nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod; if (alreadyDispatchedForVsync) { + ATRACE_FORMAT_INSTANT("alreadyDispatchedForVsync"); return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance, *mLastDispatchTime); } if (nextVsyncTooClose) { + ATRACE_FORMAT_INSTANT("nextVsyncTooClose"); return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod, *mLastDispatchTime + currentPeriod); } @@ -152,9 +168,11 @@ nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker, return nextVsyncTime; } -auto VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now, - VSyncDispatch::ScheduleTiming timing, - std::optional<ArmingInfo> armedInfo) const -> ArmingInfo { +auto VSyncDispatchTimerQueueEntry::getArmedInfo(VSyncTracker& tracker, nsecs_t now, + VSyncDispatch::ScheduleTiming timing, + std::optional<ArmingInfo> armedInfo) const + -> ArmingInfo { + ATRACE_NAME("VSyncDispatchTimerQueueEntry::getArmedInfo"); const auto earliestReadyBy = now + timing.workDuration + timing.readyDuration; const auto earliestVsync = std::max(earliestReadyBy, timing.lastVsync); @@ -165,29 +183,39 @@ auto VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now, const auto nextReadyTime = nextVsyncTime - timing.readyDuration; const auto nextWakeupTime = nextReadyTime - timing.workDuration; - bool const wouldSkipAVsyncTarget = - armedInfo && (nextVsyncTime > (armedInfo->mActualVsyncTime + mMinVsyncDistance)); - bool const wouldSkipAWakeup = - armedInfo && (nextWakeupTime > (armedInfo->mActualWakeupTime + mMinVsyncDistance)); - if (FlagManager::getInstance().dont_skip_on_early_ro() && - (wouldSkipAVsyncTarget || wouldSkipAWakeup)) { - return *armedInfo; + if (FlagManager::getInstance().dont_skip_on_early_ro()) { + bool const wouldSkipAVsyncTarget = + armedInfo && (nextVsyncTime > (armedInfo->mActualVsyncTime + mMinVsyncDistance)); + bool const wouldSkipAWakeup = + armedInfo && (nextWakeupTime > (armedInfo->mActualWakeupTime + mMinVsyncDistance)); + ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(), + wouldSkipAVsyncTarget, wouldSkipAWakeup); + if (wouldSkipAVsyncTarget || wouldSkipAWakeup) { + return *armedInfo; + } } return ArmingInfo{nextWakeupTime, nextVsyncTime, nextReadyTime}; } void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { + ATRACE_NAME("VSyncDispatchTimerQueueEntry::update"); if (!mArmedInfo && !mWorkloadUpdateInfo) { return; } if (mWorkloadUpdateInfo) { + const auto workDelta = mWorkloadUpdateInfo->workDuration - mScheduleTiming.workDuration; + const auto readyDelta = mWorkloadUpdateInfo->readyDuration - mScheduleTiming.readyDuration; + const auto lastVsyncDelta = mWorkloadUpdateInfo->lastVsync - mScheduleTiming.lastVsync; + ATRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64 + " lastVsyncDelta=%" PRId64, + workDelta, readyDelta, lastVsyncDelta); mScheduleTiming = *mWorkloadUpdateInfo; mWorkloadUpdateInfo.reset(); } - mArmedInfo = update(tracker, now, mScheduleTiming, mArmedInfo); + mArmedInfo = getArmedInfo(tracker, now, mScheduleTiming, mArmedInfo); } void VSyncDispatchTimerQueueEntry::disarm() { @@ -282,6 +310,7 @@ void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) { void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( nsecs_t now, CallbackMap::const_iterator skipUpdateIt) { + ATRACE_CALL(); std::optional<nsecs_t> min; std::optional<nsecs_t> targetVsync; std::optional<std::string_view> nextWakeupName; @@ -294,7 +323,10 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( if (it != skipUpdateIt) { callback->update(*mTracker, now); } - auto const wakeupTime = *callback->wakeupTime(); + + traceEntry(*callback, now); + + const auto wakeupTime = *callback->wakeupTime(); if (!min || *min > wakeupTime) { nextWakeupName = callback->name(); min = wakeupTime; @@ -303,11 +335,6 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( } if (min && min < mIntendedWakeupTime) { - if (ATRACE_ENABLED() && nextWakeupName && targetVsync) { - ftl::Concat trace(ftl::truncated<5>(*nextWakeupName), " alarm in ", ns2us(*min - now), - "us; VSYNC in ", ns2us(*targetVsync - now), "us"); - ATRACE_NAME(trace.c_str()); - } setTimer(*min, now); } else { ATRACE_NAME("cancel timer"); @@ -316,6 +343,7 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( } void VSyncDispatchTimerQueue::timerCallback() { + ATRACE_CALL(); struct Invocation { std::shared_ptr<VSyncDispatchTimerQueueEntry> callback; nsecs_t vsyncTimestamp; @@ -338,8 +366,9 @@ void VSyncDispatchTimerQueue::timerCallback() { continue; } - auto const readyTime = callback->readyTime(); + traceEntry(*callback, now); + auto const readyTime = callback->readyTime(); auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0)); if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) { callback->executing(); @@ -353,6 +382,8 @@ void VSyncDispatchTimerQueue::timerCallback() { } for (auto const& invocation : invocations) { + ftl::Concat trace(ftl::truncated<5>(invocation.callback->name())); + ATRACE_FORMAT("%s: %s", __func__, trace.c_str()); invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp, invocation.deadlineTimestamp); } diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h index 252c09ce53..e4ddc03480 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h @@ -91,8 +91,8 @@ private: }; nsecs_t adjustVsyncIfNeeded(VSyncTracker& tracker, nsecs_t nextVsyncTime) const; - ArmingInfo update(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming, - std::optional<ArmingInfo>) const; + ArmingInfo getArmedInfo(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming, + std::optional<ArmingInfo>) const; const std::string mName; const VSyncDispatch::Callback mCallback; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 8697696915..db1930da54 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -45,16 +45,28 @@ using base::StringAppendF; static auto constexpr kMaxPercent = 100u; +namespace { +int numVsyncsPerFrame(const ftl::NonNull<DisplayModePtr>& displayModePtr) { + const auto idealPeakRefreshPeriod = displayModePtr->getPeakFps().getPeriodNsecs(); + const auto idealRefreshPeriod = displayModePtr->getVsyncRate().getPeriodNsecs(); + return static_cast<int>(std::round(static_cast<float>(idealPeakRefreshPeriod) / + static_cast<float>(idealRefreshPeriod))); +} +} // namespace + VSyncPredictor::~VSyncPredictor() = default; -VSyncPredictor::VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, - size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent) - : mId(modePtr->getPhysicalDisplayId()), +VSyncPredictor::VSyncPredictor(std::unique_ptr<Clock> clock, ftl::NonNull<DisplayModePtr> modePtr, + size_t historySize, size_t minimumSamplesForPrediction, + uint32_t outlierTolerancePercent) + : mClock(std::move(clock)), + mId(modePtr->getPhysicalDisplayId()), mTraceOn(property_get_bool("debug.sf.vsp_trace", false)), kHistorySize(historySize), kMinimumSamplesForPrediction(minimumSamplesForPrediction), kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)), - mDisplayModePtr(modePtr) { + mDisplayModePtr(modePtr), + mNumVsyncsForFrame(numVsyncsPerFrame(mDisplayModePtr)) { resetModel(); } @@ -118,11 +130,8 @@ Period VSyncPredictor::minFramePeriod() const { } Period VSyncPredictor::minFramePeriodLocked() const { - const auto idealPeakRefreshPeriod = mDisplayModePtr->getPeakFps().getPeriodNsecs(); - const auto numPeriods = static_cast<int>(std::round(static_cast<float>(idealPeakRefreshPeriod) / - static_cast<float>(idealPeriod()))); const auto slope = mRateMap.find(idealPeriod())->second.slope; - return Period::fromNs(slope * numPeriods); + return Period::fromNs(slope * mNumVsyncsForFrame); } bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { @@ -147,7 +156,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { mKnownTimestamp = timestamp; } ATRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago", - (systemTime() - *mKnownTimestamp) / 1e6f); + (mClock->now() - *mKnownTimestamp) / 1e6f); return false; } @@ -250,17 +259,6 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { return true; } -auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSequence { - const auto vsync = snapToVsync(timestamp); - if (!mLastVsyncSequence) return {vsync, 0}; - - const auto [slope, _] = getVSyncPredictionModelLocked(); - const auto [lastVsyncTime, lastVsyncSequence] = *mLastVsyncSequence; - const auto vsyncSequence = lastVsyncSequence + - static_cast<int64_t>(std::round((vsync - lastVsyncTime) / static_cast<float>(slope))); - return {vsync, vsyncSequence}; -} - nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const { auto const [slope, intercept] = getVSyncPredictionModelLocked(); @@ -298,51 +296,45 @@ nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const { } nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, - std::optional<nsecs_t> lastVsyncOpt) const { + std::optional<nsecs_t> lastVsyncOpt) { ATRACE_CALL(); std::lock_guard lock(mMutex); - const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; - const auto threshold = currentPeriod / 2; - const auto minFramePeriod = minFramePeriodLocked().ns(); - const auto lastFrameMissed = - lastVsyncOpt && std::abs(*lastVsyncOpt - mLastMissedVsync.ns()) < threshold; - const nsecs_t baseTime = - FlagManager::getInstance().vrr_config() && !lastFrameMissed && lastVsyncOpt - ? std::max(timePoint, *lastVsyncOpt + minFramePeriod - threshold) - : timePoint; - return snapToVsyncAlignedWithRenderRate(baseTime); -} -nsecs_t VSyncPredictor::snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const { - // update the mLastVsyncSequence for reference point - mLastVsyncSequence = getVsyncSequenceLocked(timePoint); + const auto now = TimePoint::fromNs(mClock->now()); + purgeTimelines(now); - const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int { - if (!mRenderRateOpt) return 0; - const auto divisor = - RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()), - *mRenderRateOpt); - if (divisor <= 1) return 0; + if (lastVsyncOpt && *lastVsyncOpt > timePoint) { + timePoint = *lastVsyncOpt; + } - int mod = mLastVsyncSequence->seq % divisor; - if (mod == 0) return 0; + const auto model = getVSyncPredictionModelLocked(); + const auto threshold = model.slope / 2; + std::optional<Period> minFramePeriodOpt; - // This is actually a bug fix, but guarded with vrr_config since we found it with this - // config - if (FlagManager::getInstance().vrr_config()) { - if (mod < 0) mod += divisor; - } + if (mNumVsyncsForFrame > 1) { + minFramePeriodOpt = minFramePeriodLocked(); + } - return divisor - mod; - }(); + std::optional<TimePoint> vsyncOpt; + for (auto& timeline : mTimelines) { + vsyncOpt = timeline.nextAnticipatedVSyncTimeFrom(model, minFramePeriodOpt, + snapToVsync(timePoint), mMissedVsync, + lastVsyncOpt ? snapToVsync(*lastVsyncOpt - + threshold) + : lastVsyncOpt); + if (vsyncOpt) { + break; + } + } + LOG_ALWAYS_FATAL_IF(!vsyncOpt); - if (renderRatePhase == 0) { - return mLastVsyncSequence->vsyncTime; + if (*vsyncOpt > mLastCommittedVsync) { + mLastCommittedVsync = *vsyncOpt; + ATRACE_FORMAT_INSTANT("mLastCommittedVsync in %.2fms", + float(mLastCommittedVsync.ns() - mClock->now()) / 1e6f); } - auto const [slope, intercept] = getVSyncPredictionModelLocked(); - const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase; - return snapToVsync(approximateNextVsync - slope / 2); + return vsyncOpt->ns(); } /* @@ -353,39 +345,56 @@ nsecs_t VSyncPredictor::snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) cons * isVSyncInPhase(33.3, 30) = false * isVSyncInPhase(50.0, 30) = true */ -bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const { +bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) { + if (timePoint == 0) { + return true; + } + std::lock_guard lock(mMutex); - const auto divisor = - RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()), - frameRate); - return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor)); -} + const auto model = getVSyncPredictionModelLocked(); + const nsecs_t period = model.slope; + const nsecs_t justBeforeTimePoint = timePoint - period / 2; + const auto now = TimePoint::fromNs(mClock->now()); + const auto vsync = snapToVsync(justBeforeTimePoint); -bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const { - const TimePoint now = TimePoint::now(); - const auto getTimePointIn = [](TimePoint now, nsecs_t timePoint) -> float { - return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now); - }; - ATRACE_FORMAT("%s timePoint in: %.2f divisor: %zu", __func__, getTimePointIn(now, timePoint), - divisor); + purgeTimelines(now); - if (divisor <= 1 || timePoint == 0) { - return true; + for (auto& timeline : mTimelines) { + if (timeline.validUntil() && timeline.validUntil()->ns() > vsync) { + return timeline.isVSyncInPhase(model, vsync, frameRate); + } } - const nsecs_t period = mRateMap[idealPeriod()].slope; - const nsecs_t justBeforeTimePoint = timePoint - period / 2; - const auto vsyncSequence = getVsyncSequenceLocked(justBeforeTimePoint); - ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64, - getTimePointIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq); - return vsyncSequence.seq % divisor == 0; + // The last timeline should always be valid + return mTimelines.back().isVSyncInPhase(model, vsync, frameRate); } -void VSyncPredictor::setRenderRate(Fps renderRate) { +void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) { ATRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str()); ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str()); std::lock_guard lock(mMutex); + const auto prevRenderRate = mRenderRateOpt; mRenderRateOpt = renderRate; + const auto renderPeriodDelta = + prevRenderRate ? prevRenderRate->getPeriodNsecs() - renderRate.getPeriodNsecs() : 0; + const bool newRenderRateIsHigher = renderPeriodDelta > renderRate.getPeriodNsecs() && + mLastCommittedVsync.ns() - mClock->now() > 2 * renderRate.getPeriodNsecs(); + if (applyImmediately) { + while (mTimelines.size() > 1) { + mTimelines.pop_front(); + } + + mTimelines.front().setRenderRate(renderRate); + } else if (newRenderRateIsHigher) { + mTimelines.clear(); + mLastCommittedVsync = TimePoint::fromNs(0); + + } else { + mTimelines.back().freeze( + TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2)); + } + mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, renderRate); + purgeTimelines(TimePoint::fromNs(mClock->now())); } void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { @@ -401,6 +410,7 @@ void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { std::lock_guard lock(mMutex); mDisplayModePtr = modePtr; + mNumVsyncsForFrame = numVsyncsPerFrame(mDisplayModePtr); traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs()); static constexpr size_t kSizeLimit = 30; @@ -415,8 +425,14 @@ void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { clearTimestamps(); } -void VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime, - TimePoint lastConfirmedPresentTime) { +Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime, + TimePoint lastConfirmedPresentTime) { + ATRACE_CALL(); + + if (mNumVsyncsForFrame <= 1) { + return 0ns; + } + const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; const auto threshold = currentPeriod / 2; const auto minFramePeriod = minFramePeriodLocked().ns(); @@ -442,17 +458,20 @@ void VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime, if (!mPastExpectedPresentTimes.empty()) { const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime); if (phase > 0ns) { - if (mLastVsyncSequence) { - mLastVsyncSequence->vsyncTime += phase.ns(); + for (auto& timeline : mTimelines) { + timeline.shiftVsyncSequence(phase); } mPastExpectedPresentTimes.clear(); + return phase; } } + + return 0ns; } void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) { - ATRACE_CALL(); + ATRACE_NAME("VSyncPredictor::onFrameBegin"); std::lock_guard lock(mMutex); if (!mDisplayModePtr->getVrrConfig()) return; @@ -482,11 +501,14 @@ void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, } } - ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime); + const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime); + if (phase > 0ns) { + mMissedVsync = {expectedPresentTime, minFramePeriodLocked()}; + } } void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) { - ATRACE_CALL(); + ATRACE_NAME("VSyncPredictor::onFrameMissed"); std::lock_guard lock(mMutex); if (!mDisplayModePtr->getVrrConfig()) return; @@ -496,14 +518,15 @@ void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) { const auto lastConfirmedPresentTime = TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod); - ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime); - mLastMissedVsync = expectedPresentTime; + const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime); + if (phase > 0ns) { + mMissedVsync = {expectedPresentTime, Duration::fromNs(0)}; + } } VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { std::lock_guard lock(mMutex); - const auto model = VSyncPredictor::getVSyncPredictionModelLocked(); - return {model.slope, model.intercept}; + return VSyncPredictor::getVSyncPredictionModelLocked(); } VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { @@ -524,6 +547,11 @@ void VSyncPredictor::clearTimestamps() { mTimestamps.clear(); mLastTimestampIndex = 0; } + + mTimelines.clear(); + mLastCommittedVsync = TimePoint::fromNs(0); + mIdealPeriod = Period::fromNs(idealPeriod()); + mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt); } bool VSyncPredictor::needsMoreSamples() const { @@ -547,6 +575,171 @@ void VSyncPredictor::dump(std::string& result) const { period / 1e6f, periodInterceptTuple.slope / 1e6f, periodInterceptTuple.intercept); } + StringAppendF(&result, "\tmTimelines.size()=%zu\n", mTimelines.size()); +} + +void VSyncPredictor::purgeTimelines(android::TimePoint now) { + const auto kEnoughFramesToBreakPhase = 5; + if (mRenderRateOpt && + mLastCommittedVsync.ns() + mRenderRateOpt->getPeriodNsecs() * kEnoughFramesToBreakPhase < + mClock->now()) { + mTimelines.clear(); + mLastCommittedVsync = TimePoint::fromNs(0); + mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt); + return; + } + + while (mTimelines.size() > 1) { + const auto validUntilOpt = mTimelines.front().validUntil(); + if (validUntilOpt && *validUntilOpt < now) { + mTimelines.pop_front(); + } else { + break; + } + } + LOG_ALWAYS_FATAL_IF(mTimelines.empty()); + LOG_ALWAYS_FATAL_IF(mTimelines.back().validUntil().has_value()); +} + +auto VSyncPredictor::VsyncTimeline::makeVsyncSequence(TimePoint knownVsync) + -> std::optional<VsyncSequence> { + if (knownVsync.ns() == 0) return std::nullopt; + return std::make_optional<VsyncSequence>({knownVsync.ns(), 0}); +} + +VSyncPredictor::VsyncTimeline::VsyncTimeline(TimePoint knownVsync, Period idealPeriod, + std::optional<Fps> renderRateOpt) + : mIdealPeriod(idealPeriod), + mRenderRateOpt(renderRateOpt), + mLastVsyncSequence(makeVsyncSequence(knownVsync)) {} + +void VSyncPredictor::VsyncTimeline::freeze(TimePoint lastVsync) { + LOG_ALWAYS_FATAL_IF(mValidUntil.has_value()); + ATRACE_FORMAT_INSTANT("renderRate %s valid for %.2f", + mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA", + float(lastVsync.ns() - TimePoint::now().ns()) / 1e6f); + mValidUntil = lastVsync; +} + +std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTimeFrom( + Model model, std::optional<Period> minFramePeriodOpt, nsecs_t vsync, + MissedVsync missedVsync, std::optional<nsecs_t> lastVsyncOpt) { + ATRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA"); + + nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync); + const auto threshold = model.slope / 2; + const auto lastFrameMissed = + lastVsyncOpt && std::abs(*lastVsyncOpt - missedVsync.vsync.ns()) < threshold; + nsecs_t vsyncFixupTime = 0; + if (FlagManager::getInstance().vrr_config() && lastFrameMissed) { + // If the last frame missed is the last vsync, we already shifted the timeline. Depends on + // whether we skipped the frame (onFrameMissed) or not (onFrameBegin) we apply a different + // fixup. There is no need to to shift the vsync timeline again. + vsyncTime += missedVsync.fixup.ns(); + ATRACE_FORMAT_INSTANT("lastFrameMissed"); + } else if (minFramePeriodOpt) { + if (FlagManager::getInstance().vrr_config() && lastVsyncOpt) { + // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it + // first before trying to use it. + if (mLastVsyncSequence->seq > 0) { + lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt); + } + const auto vsyncDiff = vsyncTime - *lastVsyncOpt; + if (vsyncDiff <= minFramePeriodOpt->ns() - threshold) { + vsyncFixupTime = *lastVsyncOpt + minFramePeriodOpt->ns() - vsyncTime; + ATRACE_FORMAT_INSTANT("minFramePeriod violation. next in %.2f which is %.2f " + "from " + "prev. " + "adjust by %.2f", + static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f, + static_cast<float>(vsyncTime - *lastVsyncOpt) / 1e6f, + static_cast<float>(vsyncFixupTime) / 1e6f); + } + } + vsyncTime += vsyncFixupTime; + } + + ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f); + if (mValidUntil && vsyncTime > mValidUntil->ns()) { + ATRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f", + static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f); + return std::nullopt; + } + + // If we needed a fixup, it means that we changed the render rate and the chosen vsync would + // cross minFramePeriod. In that case we need to shift the entire vsync timeline. + if (vsyncFixupTime > 0) { + shiftVsyncSequence(Duration::fromNs(vsyncFixupTime)); + } + + return TimePoint::fromNs(vsyncTime); +} + +auto VSyncPredictor::VsyncTimeline::getVsyncSequenceLocked(Model model, nsecs_t vsync) + -> VsyncSequence { + if (!mLastVsyncSequence) return {vsync, 0}; + + const auto [lastVsyncTime, lastVsyncSequence] = *mLastVsyncSequence; + const auto vsyncSequence = lastVsyncSequence + + static_cast<int64_t>(std::round((vsync - lastVsyncTime) / + static_cast<float>(model.slope))); + return {vsync, vsyncSequence}; +} + +nsecs_t VSyncPredictor::VsyncTimeline::snapToVsyncAlignedWithRenderRate(Model model, + nsecs_t vsync) { + // update the mLastVsyncSequence for reference point + mLastVsyncSequence = getVsyncSequenceLocked(model, vsync); + + const auto renderRatePhase = [&]() -> int { + if (!mRenderRateOpt) return 0; + const auto divisor = + RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod.ns()), + *mRenderRateOpt); + if (divisor <= 1) return 0; + + int mod = mLastVsyncSequence->seq % divisor; + if (mod == 0) return 0; + + // This is actually a bug fix, but guarded with vrr_config since we found it with this + // config + if (FlagManager::getInstance().vrr_config()) { + if (mod < 0) mod += divisor; + } + + return divisor - mod; + }(); + + if (renderRatePhase == 0) { + return mLastVsyncSequence->vsyncTime; + } + + return mLastVsyncSequence->vsyncTime + model.slope * renderRatePhase; +} + +bool VSyncPredictor::VsyncTimeline::isVSyncInPhase(Model model, nsecs_t vsync, Fps frameRate) { + const auto getVsyncIn = [](TimePoint now, nsecs_t timePoint) -> float { + return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now); + }; + + Fps displayFps = mRenderRateOpt ? *mRenderRateOpt : Fps::fromPeriodNsecs(mIdealPeriod.ns()); + const auto divisor = RefreshRateSelector::getFrameRateDivisor(displayFps, frameRate); + const auto now = TimePoint::now(); + + if (divisor <= 1) { + return true; + } + const auto vsyncSequence = getVsyncSequenceLocked(model, vsync); + ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64 " divisor: %zu", + getVsyncIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq, divisor); + return vsyncSequence.seq % divisor == 0; +} + +void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase) { + if (mLastVsyncSequence) { + ATRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f); + mLastVsyncSequence->vsyncTime += phase.ns(); + } } } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 8fd7e6046d..3ed1d41a01 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -22,6 +22,7 @@ #include <vector> #include <android-base/thread_annotations.h> +#include <scheduler/TimeKeeper.h> #include <ui/DisplayId.h> #include "VSyncTracker.h" @@ -31,6 +32,7 @@ namespace android::scheduler { class VSyncPredictor : public VSyncTracker { public: /* + * \param [in] Clock The clock abstraction. Useful for unit tests. * \param [in] PhysicalDisplayid The display this corresponds to. * \param [in] modePtr The initial display mode * \param [in] historySize The internal amount of entries to store in the model. @@ -38,13 +40,13 @@ public: * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter * samples that fall outlierTolerancePercent from an anticipated vsync event. */ - VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, + VSyncPredictor(std::unique_ptr<Clock>, ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent); ~VSyncPredictor(); bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex); nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, - std::optional<nsecs_t> lastVsyncOpt = {}) const final + std::optional<nsecs_t> lastVsyncOpt = {}) final EXCLUDES(mMutex); nsecs_t currentPeriod() const final EXCLUDES(mMutex); Period minFramePeriod() const final EXCLUDES(mMutex); @@ -62,11 +64,11 @@ public: VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex); - bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex); + bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) final EXCLUDES(mMutex); void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final EXCLUDES(mMutex); - void setRenderRate(Fps) final EXCLUDES(mMutex); + void setRenderRate(Fps, bool applyImmediately) final EXCLUDES(mMutex); void onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) final EXCLUDES(mMutex); @@ -75,10 +77,44 @@ public: void dump(std::string& result) const final EXCLUDES(mMutex); private: + struct VsyncSequence { + nsecs_t vsyncTime; + int64_t seq; + }; + + struct MissedVsync { + TimePoint vsync; + Duration fixup = Duration::fromNs(0); + }; + + class VsyncTimeline { + public: + VsyncTimeline(TimePoint knownVsync, Period idealPeriod, std::optional<Fps> renderRateOpt); + std::optional<TimePoint> nextAnticipatedVSyncTimeFrom( + Model model, std::optional<Period> minFramePeriodOpt, nsecs_t vsyncTime, + MissedVsync lastMissedVsync, std::optional<nsecs_t> lastVsyncOpt = {}); + void freeze(TimePoint lastVsync); + std::optional<TimePoint> validUntil() const { return mValidUntil; } + bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate); + void shiftVsyncSequence(Duration phase); + void setRenderRate(Fps renderRate) { mRenderRateOpt = renderRate; } + + private: + nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync); + VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync); + std::optional<VsyncSequence> makeVsyncSequence(TimePoint knownVsync); + + const Period mIdealPeriod = Duration::fromNs(0); + std::optional<Fps> mRenderRateOpt; + std::optional<TimePoint> mValidUntil; + std::optional<VsyncSequence> mLastVsyncSequence; + }; + VSyncPredictor(VSyncPredictor const&) = delete; VSyncPredictor& operator=(VSyncPredictor const&) = delete; void clearTimestamps() REQUIRES(mMutex); + const std::unique_ptr<Clock> mClock; const PhysicalDisplayId mId; inline void traceInt64If(const char* name, int64_t value) const; @@ -88,16 +124,10 @@ private: bool validate(nsecs_t timestamp) const REQUIRES(mMutex); Model getVSyncPredictionModelLocked() const REQUIRES(mMutex); nsecs_t snapToVsync(nsecs_t timePoint) const REQUIRES(mMutex); - nsecs_t snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const REQUIRES(mMutex); - bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex); Period minFramePeriodLocked() const REQUIRES(mMutex); - void ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex); + Duration ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex); + void purgeTimelines(android::TimePoint now) REQUIRES(mMutex); - struct VsyncSequence { - nsecs_t vsyncTime; - int64_t seq; - }; - VsyncSequence getVsyncSequenceLocked(nsecs_t timestamp) const REQUIRES(mMutex); nsecs_t idealPeriod() const REQUIRES(mMutex); bool const mTraceOn; @@ -115,13 +145,16 @@ private: std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex); ftl::NonNull<DisplayModePtr> mDisplayModePtr GUARDED_BY(mMutex); - std::optional<Fps> mRenderRateOpt GUARDED_BY(mMutex); - - mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex); + int mNumVsyncsForFrame GUARDED_BY(mMutex); std::deque<TimePoint> mPastExpectedPresentTimes GUARDED_BY(mMutex); - TimePoint mLastMissedVsync GUARDED_BY(mMutex); + MissedVsync mMissedVsync GUARDED_BY(mMutex); + + std::deque<VsyncTimeline> mTimelines GUARDED_BY(mMutex); + TimePoint mLastCommittedVsync GUARDED_BY(mMutex) = TimePoint::fromNs(0); + Period mIdealPeriod GUARDED_BY(mMutex) = Duration::fromNs(0); + std::optional<Fps> mRenderRateOpt GUARDED_BY(mMutex); }; } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 37bd4b4977..8787cdb82f 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -56,8 +56,8 @@ public: * and avoid crossing the minimal frame period of a VRR display. * \return A prediction of the timestamp of a vsync event. */ - virtual nsecs_t nextAnticipatedVSyncTimeFrom( - nsecs_t timePoint, std::optional<nsecs_t> lastVsyncOpt = {}) const = 0; + virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, + std::optional<nsecs_t> lastVsyncOpt = {}) = 0; /* * The current period of the vsync signal. @@ -82,7 +82,7 @@ public: * \param [in] timePoint A vsync timestamp * \param [in] frameRate The frame rate to check for */ - virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0; + virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) = 0; /* * Sets the active mode of the display which includes the vsync period and other VRR attributes. @@ -102,8 +102,10 @@ public: * when a display is running at 120Hz but the render frame rate is 60Hz. * * \param [in] Fps The render rate the tracker should operate at. + * \param [in] applyImmediately Whether to apply the new render rate immediately regardless of + * already committed vsyncs. */ - virtual void setRenderRate(Fps) = 0; + virtual void setRenderRate(Fps, bool applyImmediately) = 0; virtual void onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) = 0; diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index 001938c756..2fa3318560 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -120,8 +120,8 @@ VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(ftl::NonNull<DisplayModeP constexpr size_t kMinSamplesForPrediction = 6; constexpr uint32_t kDiscardOutlierPercent = 20; - return std::make_unique<VSyncPredictor>(modePtr, kHistorySize, kMinSamplesForPrediction, - kDiscardOutlierPercent); + return std::make_unique<VSyncPredictor>(std::make_unique<SystemClock>(), modePtr, kHistorySize, + kMinSamplesForPrediction, kDiscardOutlierPercent); } VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) { diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index 85cd3e7c31..881d6789b2 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -81,7 +81,7 @@ public: bool addResyncSample(TimePoint timestamp, ftl::Optional<Period> hwcVsyncPeriod); // TODO(b/185535769): Hide behind API. - const VsyncTracker& getTracker() const { return *mTracker; } + VsyncTracker& getTracker() const { return *mTracker; } VsyncTracker& getTracker() { return *mTracker; } VsyncController& getController() { return *mController; } diff --git a/services/surfaceflinger/StartPropertySetThread.cpp b/services/surfaceflinger/StartPropertySetThread.cpp deleted file mode 100644 index f42cd53e0a..0000000000 --- a/services/surfaceflinger/StartPropertySetThread.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <cutils/properties.h> -#include "StartPropertySetThread.h" - -namespace android { - -StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue): - Thread(false), mTimestampPropertyValue(timestampPropertyValue) {} - -status_t StartPropertySetThread::Start() { - return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL); -} - -bool StartPropertySetThread::threadLoop() { - // Set property service.sf.present_timestamp, consumer need check its readiness - property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0"); - // Clear BootAnimation exit flag - property_set("service.bootanim.exit", "0"); - property_set("service.bootanim.progress", "0"); - // Start BootAnimation if not started - property_set("ctl.start", "bootanim"); - // Exit immediately - return false; -} - -} // namespace android diff --git a/services/surfaceflinger/StartPropertySetThread.h b/services/surfaceflinger/StartPropertySetThread.h deleted file mode 100644 index bbdcde2809..0000000000 --- a/services/surfaceflinger/StartPropertySetThread.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_STARTBOOTANIMTHREAD_H -#define ANDROID_STARTBOOTANIMTHREAD_H - -#include <stddef.h> - -#include <utils/Mutex.h> -#include <utils/Thread.h> - -namespace android { - -class StartPropertySetThread : public Thread { -// Boot animation is triggered via calls to "property_set()" which can block -// if init's executing slow operation such as 'mount_all --late' (currently -// happening 1/10th with fsck) concurrently. Running in a separate thread -// allows to pursue the SurfaceFlinger's init process without blocking. -// see b/34499826. -// Any property_set() will block during init stage so need to be offloaded -// to this thread. see b/63844978. -public: - explicit StartPropertySetThread(bool timestampPropertyValue); - status_t Start(); -private: - virtual bool threadLoop(); - static constexpr const char* kTimestampProperty = "service.sf.present_timestamp"; - const bool mTimestampPropertyValue; -}; - -} - -#endif // ANDROID_STARTBOOTANIMTHREAD_H diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 2501f4b7f7..bf210afe6d 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -153,7 +153,6 @@ #include "Scheduler/VsyncConfiguration.h" #include "Scheduler/VsyncModulator.h" #include "ScreenCaptureOutput.h" -#include "StartPropertySetThread.h" #include "SurfaceFlingerProperties.h" #include "TimeStats/TimeStats.h" #include "TunnelModeEnabledReporter.h" @@ -566,7 +565,11 @@ void SurfaceFlinger::binderDied(const wp<IBinder>&) { initializeDisplays(); })); - startBootAnim(); + mInitBootPropsFuture.callOnce([this] { + return std::async(std::launch::async, &SurfaceFlinger::initBootProperties, this); + }); + + mInitBootPropsFuture.wait(); } void SurfaceFlinger::run() { @@ -722,13 +725,10 @@ void SurfaceFlinger::bootFinished() { } mBootFinished = true; FlagManager::getMutableInstance().markBootCompleted(); - if (mStartPropertySetThread->join() != NO_ERROR) { - ALOGE("Join StartPropertySetThread failed!"); - } - if (mRenderEnginePrimeCacheFuture.valid()) { - mRenderEnginePrimeCacheFuture.get(); - } + mInitBootPropsFuture.wait(); + mRenderEnginePrimeCacheFuture.wait(); + const nsecs_t now = systemTime(); const nsecs_t duration = now - mBootTime; ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); @@ -816,8 +816,6 @@ void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& bui } } -// Do not call property_set on main thread which will be blocked by init -// Use StartPropertySetThread instead. void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { ATRACE_CALL(); ALOGI( "SurfaceFlinger's main thread ready to run. " @@ -919,27 +917,40 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { ALOGW("Can't set SCHED_OTHER for primeCache"); } - bool shouldPrimeUltraHDR = - base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false); - mRenderEnginePrimeCacheFuture = getRenderEngine().primeCache(shouldPrimeUltraHDR); + mRenderEnginePrimeCacheFuture.callOnce([this] { + const bool shouldPrimeUltraHDR = + base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false); + return getRenderEngine().primeCache(shouldPrimeUltraHDR); + }); if (setSchedFifo(true) != NO_ERROR) { ALOGW("Can't set SCHED_FIFO after primeCache"); } } - // Inform native graphics APIs whether the present timestamp is supported: - - mStartPropertySetThread = getFactory().createStartPropertySetThread(mHasReliablePresentFences); - - if (mStartPropertySetThread->Start() != NO_ERROR) { - ALOGE("Run StartPropertySetThread failed!"); - } + // Avoid blocking the main thread on `init` to set properties. + mInitBootPropsFuture.callOnce([this] { + return std::async(std::launch::async, &SurfaceFlinger::initBootProperties, this); + }); initTransactionTraceWriter(); ALOGV("Done initializing"); } +// During boot, offload `initBootProperties` to another thread. `property_set` depends on +// `property_service`, which may be delayed by slow operations like `mount_all --late` in +// the `init` process. See b/34499826 and b/63844978. +void SurfaceFlinger::initBootProperties() { + property_set("service.sf.present_timestamp", mHasReliablePresentFences ? "1" : "0"); + + if (base::GetBoolProperty("debug.sf.boot_animation"s, true)) { + // Reset and (if needed) start BootAnimation. + property_set("service.bootanim.exit", "0"); + property_set("service.bootanim.progress", "0"); + property_set("ctl.start", "bootanim"); + } +} + void SurfaceFlinger::initTransactionTraceWriter() { if (!mTransactionTracing) { return; @@ -979,18 +990,6 @@ void SurfaceFlinger::readPersistentProperties() { static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0)); } -void SurfaceFlinger::startBootAnim() { - // Start boot animation service by setting a property mailbox - // if property setting thread is already running, Start() will be just a NOP - mStartPropertySetThread->Start(); - // Wait until property was set - if (mStartPropertySetThread->join() != NO_ERROR) { - ALOGE("Join StartPropertySetThread failed!"); - } -} - -// ---------------------------------------------------------------------------- - status_t SurfaceFlinger::getSupportedFrameTimestamps( std::vector<FrameEvent>* outSupported) const { *outSupported = { @@ -1239,8 +1238,8 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { switch (display->setDesiredMode(std::move(desiredMode))) { case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch: // DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler. - mScheduler->setRenderRate(displayId, - display->refreshRateSelector().getActiveMode().fps); + mScheduler->setRenderRate(displayId, display->refreshRateSelector().getActiveMode().fps, + /*applyImmediately*/ true); // Schedule a new frame to initiate the display mode switch. scheduleComposite(FrameHint::kNone); @@ -1261,7 +1260,7 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { mScheduler->setModeChangePending(true); break; case DisplayDevice::DesiredModeAction::InitiateRenderRateSwitch: - mScheduler->setRenderRate(displayId, mode.fps); + mScheduler->setRenderRate(displayId, mode.fps, /*applyImmediately*/ false); if (displayId == mActiveDisplayId) { mScheduler->updatePhaseConfiguration(mode.fps); @@ -1382,7 +1381,7 @@ void SurfaceFlinger::applyActiveMode(const sp<DisplayDevice>& display) { constexpr bool kAllowToEnable = true; mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take()); - mScheduler->setRenderRate(displayId, renderFps); + mScheduler->setRenderRate(displayId, renderFps, /*applyImmediately*/ true); if (displayId == mActiveDisplayId) { mScheduler->updatePhaseConfiguration(renderFps); @@ -2730,7 +2729,8 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( refreshArgs.forceOutputColorMode = mForceColorMode; refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty; - refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty; + refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || + mVisibleRegionsDirty || mDrawingState.colorMatrixChanged; refreshArgs.internalDisplayRotationFlags = getActiveDisplayRotationFlags(); if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) { @@ -2783,12 +2783,16 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( } } + refreshArgs.refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + for (auto [layer, layerFE] : layers) { + layer->onPreComposition(refreshArgs.refreshStartTime); + } + mCompositionEngine->present(refreshArgs); moveSnapshotsFromCompositionArgs(refreshArgs, layers); for (auto [layer, layerFE] : layers) { CompositionResult compositionResult{layerFE->stealCompositionResult()}; - layer->onPreComposition(compositionResult.refreshStartTime); for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) { Layer* clonedFrom = layer->getClonedFrom().get(); auto owningLayer = clonedFrom ? clonedFrom : layer; @@ -4376,7 +4380,8 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { // The pacesetter must be registered before EventThread creation below. mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); if (FlagManager::getInstance().vrr_config()) { - mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps); + mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps, + /*applyImmediately*/ true); } const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs(); @@ -4700,7 +4705,14 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelin return TransactionReadiness::NotReady; } - if (!mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) { + const auto vsyncId = VsyncId{transaction.frameTimelineInfo.vsyncId}; + + // Transactions with VsyncId are already throttled by the vsyncId (i.e. Choreographer issued + // the vsyncId according to the frame rate override cadence) so we shouldn't throttle again + // when applying the transaction. Otherwise we might throttle older transactions + // incorrectly as the frame rate of SF changed before it drained the older transactions. + if (ftl::to_underlying(vsyncId) == FrameTimelineInfo::INVALID_VSYNC_ID && + !mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) { ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d", expectedPresentTime, transaction.originUid); return TransactionReadiness::NotReady; @@ -4708,8 +4720,7 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelin // If the client didn't specify desiredPresentTime, use the vsyncId to determine the // expected present time of this transaction. - if (transaction.isAutoTimestamp && - frameIsEarly(expectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) { + if (transaction.isAutoTimestamp && frameIsEarly(expectedPresentTime, vsyncId)) { ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64, transaction.frameTimelineInfo.vsyncId, expectedPresentTime); return TransactionReadiness::NotReady; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 005f2e6344..0cc8fbb98a 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -89,6 +89,7 @@ #include "Tracing/TransactionTracing.h" #include "TransactionCallbackInvoker.h" #include "TransactionState.h" +#include "Utils/OnceFuture.h" #include <atomic> #include <cstdint> @@ -550,7 +551,7 @@ private: bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) override; void bootFinished(); - virtual status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const; + status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const; sp<IDisplayEventConnection> createDisplayEventConnection( gui::ISurfaceComposer::VsyncSource vsyncSource = gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp, @@ -871,9 +872,6 @@ private: // Traverse through all the layers and compute and cache its bounds. void computeLayerBounds(); - // Boot animation, on/off animations and screen capture - void startBootAnim(); - bool layersHasProtectedLayer(const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const; void captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction, ui::Size bufferSize, @@ -1184,11 +1182,17 @@ private: ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const REQUIRES(mStateLock); void traverseLegacyLayers(const LayerVector::Visitor& visitor) const; + + void initBootProperties(); void initTransactionTraceWriter(); - sp<StartPropertySetThread> mStartPropertySetThread; + surfaceflinger::Factory& mFactory; pid_t mPid; - std::future<void> mRenderEnginePrimeCacheFuture; + + // TODO: b/328459745 - Encapsulate in a SystemProperties object. + utils::OnceFuture mInitBootPropsFuture; + + utils::OnceFuture mRenderEnginePrimeCacheFuture; // mStateLock has conventions related to the current thread, because only // the main thread should modify variables protected by mStateLock. diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp index 7e6894d3f7..50b167d5d7 100644 --- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp @@ -26,7 +26,6 @@ #include "FrameTracer/FrameTracer.h" #include "Layer.h" #include "NativeWindowSurface.h" -#include "StartPropertySetThread.h" #include "SurfaceFlingerDefaultFactory.h" #include "SurfaceFlingerProperties.h" @@ -53,11 +52,6 @@ std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfig } } -sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread( - bool timestampPropertyValue) { - return sp<StartPropertySetThread>::make(timestampPropertyValue); -} - sp<DisplayDevice> DefaultFactory::createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) { return sp<DisplayDevice>::make(creationArgs); } diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h index 2c6de0e113..540dec832e 100644 --- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h @@ -29,7 +29,6 @@ public: std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override; std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( Fps currentRefreshRate) override; - sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override; sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override; sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage, diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h index f310c4ac53..f1fbf013c7 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.h +++ b/services/surfaceflinger/SurfaceFlingerFactory.h @@ -39,7 +39,6 @@ class IGraphicBufferConsumer; class IGraphicBufferProducer; class Layer; class LayerFE; -class StartPropertySetThread; class SurfaceFlinger; class TimeStats; @@ -71,8 +70,6 @@ public: virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( Fps currentRefreshRate) = 0; - virtual sp<StartPropertySetThread> createStartPropertySetThread( - bool timestampPropertyValue) = 0; virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) = 0; virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, diff --git a/services/surfaceflinger/Utils/OnceFuture.h b/services/surfaceflinger/Utils/OnceFuture.h new file mode 100644 index 0000000000..412038ce10 --- /dev/null +++ b/services/surfaceflinger/Utils/OnceFuture.h @@ -0,0 +1,53 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <future> +#include <mutex> + +#include <android-base/thread_annotations.h> + +namespace android::utils { + +// Allows a thread to `wait` for a future produced by a different thread. The future is returned by +// the first call to a function `F` that multiple threads may `callOnce`. If no `callOnce` happens, +// then `wait` does nothing. Otherwise, it blocks on the future, then destroys it, which resets the +// `OnceFuture`. +class OnceFuture { +public: + template <typename F> + void callOnce(F f) { + std::lock_guard lock(mMutex); + if (!mFuture.valid()) { + mFuture = f(); + } + } + + void wait() { + std::lock_guard lock(mMutex); + if (mFuture.valid()) { + mFuture.wait(); + mFuture = {}; + } + } + +private: + std::mutex mMutex; + std::future<void> mFuture GUARDED_BY(mMutex); +}; + +} // namespace android::utils diff --git a/services/surfaceflinger/common/include/common/test/FlagUtils.h b/services/surfaceflinger/common/include/common/test/FlagUtils.h index 550c70d98f..d61fcb5438 100644 --- a/services/surfaceflinger/common/include/common/test/FlagUtils.h +++ b/services/surfaceflinger/common/include/common/test/FlagUtils.h @@ -18,7 +18,10 @@ #include <common/FlagManager.h> -#define SET_FLAG_FOR_TEST(name, value) TestFlagSetter _testflag_((name), (name), (value)) +#define SET_FLAG_FOR_TEST(name, value) \ + TestFlagSetter _testflag_ { \ + (name), (name), (value) \ + } namespace android { class TestFlagSetter { diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp index aecfcba6e6..867ff55632 100644 --- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp @@ -560,4 +560,36 @@ TEST_F(LayerLifecycleManagerTest, alphaChangesAlwaysSetsVisibleRegionFlag) { ftl::Flags<RequestedLayerState::Changes>().string()); } +TEST_F(LayerLifecycleManagerTest, layerSecureChangesSetsVisibilityChangeFlag) { + // add a default buffer and make the layer secure + setFlags(1, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure); + setBuffer(1, + std::make_shared<renderengine::mock:: + FakeExternalTexture>(1U /*width*/, 1U /*height*/, + 1ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_SW_READ_NEVER /*usage*/)); + + mLifecycleManager.commitChanges(); + + // set new buffer but layer secure doesn't change + setBuffer(1, + std::make_shared<renderengine::mock:: + FakeExternalTexture>(1U /*width*/, 1U /*height*/, + 2ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_SW_READ_NEVER /*usage*/)); + EXPECT_EQ(mLifecycleManager.getGlobalChanges().get(), + ftl::Flags<RequestedLayerState::Changes>(RequestedLayerState::Changes::Buffer | + RequestedLayerState::Changes::Content) + .get()); + mLifecycleManager.commitChanges(); + + // change layer flags and confirm visibility flag is set + setFlags(1, layer_state_t::eLayerSecure, 0); + EXPECT_TRUE( + mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Visibility)); + mLifecycleManager.commitChanges(); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp index 1d44a3ef77..d9343c7813 100644 --- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp +++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp @@ -18,7 +18,9 @@ #define LOG_TAG "PowerAdvisorTest" #include <DisplayHardware/PowerAdvisor.h> +#include <android_os.h> #include <binder/Status.h> +#include <common/test/FlagUtils.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <powermanager/PowerHalWrapper.h> @@ -55,6 +57,7 @@ protected: std::unique_ptr<PowerAdvisor> mPowerAdvisor; MockPowerHalController* mMockPowerHalController; std::shared_ptr<MockPowerHintSessionWrapper> mMockPowerHintSession; + SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel, true); }; bool PowerAdvisorTest::sessionExists() { @@ -75,13 +78,14 @@ void PowerAdvisorTest::SetUp() { void PowerAdvisorTest::startPowerHintSession(bool returnValidSession) { mMockPowerHintSession = std::make_shared<NiceMock<MockPowerHintSessionWrapper>>(); if (returnValidSession) { - ON_CALL(*mMockPowerHalController, createHintSession) - .WillByDefault([&](int32_t, int32_t, const std::vector<int32_t>&, int64_t) { - return HalResult<std::shared_ptr<PowerHintSessionWrapper>>:: - fromStatus(ndk::ScopedAStatus::ok(), mMockPowerHintSession); - }); + ON_CALL(*mMockPowerHalController, createHintSessionWithConfig) + .WillByDefault(DoAll(SetArgPointee<5>(aidl::android::hardware::power::SessionConfig{ + .id = 12}), + Return(HalResult<std::shared_ptr<PowerHintSessionWrapper>>:: + fromStatus(binder::Status::ok(), + mMockPowerHintSession)))); } else { - ON_CALL(*mMockPowerHalController, createHintSession).WillByDefault([] { + ON_CALL(*mMockPowerHalController, createHintSessionWithConfig).WillByDefault([] { return HalResult< std::shared_ptr<PowerHintSessionWrapper>>::fromStatus(ndk::ScopedAStatus::ok(), nullptr); @@ -287,7 +291,7 @@ TEST_F(PowerAdvisorTest, hintSessionValidWhenNullFromPowerHAL) { } TEST_F(PowerAdvisorTest, hintSessionOnlyCreatedOnce) { - EXPECT_CALL(*mMockPowerHalController, createHintSession(_, _, _, _)).Times(1); + EXPECT_CALL(*mMockPowerHalController, createHintSessionWithConfig(_, _, _, _, _, _)).Times(1); mPowerAdvisor->onBootFinished(); startPowerHintSession(); mPowerAdvisor->startPowerHintSession({1, 2, 3}); @@ -339,7 +343,7 @@ TEST_F(PowerAdvisorTest, hintSessionTestNotifyReportRace) { return HalResult<void>::fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127)); }); - ON_CALL(*mMockPowerHalController, createHintSession).WillByDefault([] { + ON_CALL(*mMockPowerHalController, createHintSessionWithConfig).WillByDefault([] { return HalResult<std::shared_ptr<PowerHintSessionWrapper>>:: fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127), nullptr); }); @@ -374,5 +378,17 @@ TEST_F(PowerAdvisorTest, hintSessionTestNotifyReportRace) { EXPECT_EQ(sessionExists(), false); } +TEST_F(PowerAdvisorTest, legacyHintSessionCreationStillWorks) { + SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel, false); + mPowerAdvisor->onBootFinished(); + mMockPowerHintSession = std::make_shared<NiceMock<MockPowerHintSessionWrapper>>(); + EXPECT_CALL(*mMockPowerHalController, createHintSession) + .Times(1) + .WillOnce(Return(HalResult<std::shared_ptr<PowerHintSessionWrapper>>:: + fromStatus(binder::Status::ok(), mMockPowerHintSession))); + mPowerAdvisor->enablePowerHintSession(true); + mPowerAdvisor->startPowerHintSession({1, 2, 3}); +} + } // namespace } // namespace android::Hwc2::impl diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 10e2220ece..d4735c7558 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -25,6 +25,7 @@ #include "Scheduler/EventThread.h" #include "Scheduler/RefreshRateSelector.h" #include "Scheduler/VSyncPredictor.h" +#include "Scheduler/VSyncReactor.h" #include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockDisplayMode.h" @@ -56,6 +57,11 @@ using LayerHierarchy = surfaceflinger::frontend::LayerHierarchy; using LayerHierarchyBuilder = surfaceflinger::frontend::LayerHierarchyBuilder; using RequestedLayerState = surfaceflinger::frontend::RequestedLayerState; +class ZeroClock : public Clock { +public: + nsecs_t now() const override { return 0; } +}; + class SchedulerTest : public testing::Test { protected: class MockEventThreadConnection : public android::EventThreadConnection { @@ -563,7 +569,8 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { hal::VrrConfig{.minFrameIntervalNs = static_cast<int32_t>( frameRate.getPeriodNsecs())})); std::shared_ptr<VSyncPredictor> vrrTracker = - std::make_shared<VSyncPredictor>(kMode, kHistorySize, kMinimumSamplesForPrediction, + std::make_shared<VSyncPredictor>(std::make_unique<ZeroClock>(), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent); std::shared_ptr<RefreshRateSelector> vrrSelectorPtr = std::make_shared<RefreshRateSelector>(makeModes(kMode), kMode->getId()); @@ -576,8 +583,10 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, vrrTracker); vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate); - scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate); + scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate, /*applyImmediately*/ false); vrrTracker->addVsyncTimestamp(0); + // Set 1000 as vsync seq #0 + vrrTracker->nextAnticipatedVSyncTimeFrom(700); EXPECT_EQ(Fps::fromPeriodNsecs(1000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), @@ -587,20 +596,21 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { TimePoint::fromNs(2000))); // Not crossing the min frame period - EXPECT_EQ(Fps::fromPeriodNsecs(1500), + vrrTracker->onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500)); + EXPECT_EQ(Fps::fromPeriodNsecs(1000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), TimePoint::fromNs(2500))); // Change render rate frameRate = Fps::fromPeriodNsecs(2000); vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate); - scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate); + scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate, /*applyImmediately*/ false); EXPECT_EQ(Fps::fromPeriodNsecs(2000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(2000))); + TimePoint::fromNs(4500))); EXPECT_EQ(Fps::fromPeriodNsecs(2000), scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), - TimePoint::fromNs(4000))); + TimePoint::fromNs(6500))); } TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) { diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index bce7729d80..82023b092a 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -43,7 +43,6 @@ #include "RenderArea.h" #include "Scheduler/MessageQueue.h" #include "Scheduler/RefreshRateSelector.h" -#include "StartPropertySetThread.h" #include "SurfaceFlinger.h" #include "TestableScheduler.h" #include "mock/DisplayHardware/MockComposer.h" @@ -94,10 +93,6 @@ public: return std::make_unique<scheduler::FakePhaseOffsets>(); } - sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override { - return sp<StartPropertySetThread>::make(timestampPropertyValue); - } - sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) override { return sp<DisplayDevice>::make(creationArgs); } diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index d891008683..d701a97b7d 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -48,9 +48,9 @@ public: Period minFramePeriod() const final { return Period::fromNs(currentPeriod()); } void resetModel() final {} bool needsMoreSamples() const final { return false; } - bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } + bool isVSyncInPhase(nsecs_t, Fps) final { return false; } void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {} - void setRenderRate(Fps) final {} + void setRenderRate(Fps, bool) final {} void onFrameBegin(TimePoint, TimePoint) final {} void onFrameMissed(TimePoint) final {} void dump(std::string&) const final {} @@ -64,7 +64,7 @@ class FixedRateIdealStubTracker : public StubTracker { public: FixedRateIdealStubTracker() : StubTracker{toNs(3ms)} {} - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, std::optional<nsecs_t>) const final { + nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, std::optional<nsecs_t>) final { auto const floor = timePoint % mPeriod; if (floor == 0) { return timePoint; @@ -77,7 +77,7 @@ class VRRStubTracker : public StubTracker { public: VRRStubTracker(nsecs_t period) : StubTracker(period) {} - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point, std::optional<nsecs_t>) const final { + nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point, std::optional<nsecs_t>) final { std::lock_guard lock(mMutex); auto const normalized_to_base = time_point - mBase; auto const floor = (normalized_to_base) % mPeriod; diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index b9f3d70c6b..48707cb6d2 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -75,6 +75,28 @@ ftl::NonNull<DisplayModePtr> displayMode(nsecs_t period) { return ftl::as_non_null(createDisplayMode(DisplayModeId(0), refreshRate, kGroup, kResolution, DEFAULT_DISPLAY_ID)); } + +class TestClock : public Clock { +public: + TestClock() = default; + + nsecs_t now() const override { return mNow; } + void setNow(nsecs_t now) { mNow = now; } + +private: + nsecs_t mNow = 0; +}; + +class ClockWrapper : public Clock { +public: + ClockWrapper(std::shared_ptr<Clock> const& clock) : mClock(clock) {} + + nsecs_t now() const { return mClock->now(); } + +private: + std::shared_ptr<Clock> const mClock; +}; + } // namespace struct VSyncPredictorTest : testing::Test { @@ -86,8 +108,10 @@ struct VSyncPredictorTest : testing::Test { static constexpr size_t kOutlierTolerancePercent = 25; static constexpr nsecs_t mMaxRoundingError = 100; - VSyncPredictor tracker{mMode, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent}; + std::shared_ptr<TestClock> mClock{std::make_shared<TestClock>()}; + + VSyncPredictor tracker{std::make_unique<ClockWrapper>(mClock), mMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; }; TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { @@ -408,7 +432,8 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { // See b/151146131 TEST_F(VSyncPredictorTest, hasEnoughPrecision) { const auto mode = displayMode(mPeriod); - VSyncPredictor tracker{mode, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + VSyncPredictor tracker{std::make_unique<ClockWrapper>(mClock), mode, 20, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675, 840923581635, 840940161584, 840956868096, 840973702473, 840990256277, 841007116851, @@ -595,44 +620,15 @@ TEST_F(VSyncPredictorTest, setRenderRateIsRespected) { tracker.addVsyncTimestamp(mNow); } - tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod)); - - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 4 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 4 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 7 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod)); -} - -TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) { - auto last = mNow; - for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); - mNow += mPeriod; - last = mNow; - tracker.addVsyncTimestamp(mNow); - } - - const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); + tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod), /*applyImmediately*/ false); - tracker.setRenderRate(refreshRate / 4); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 7 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 11 * mPeriod)); - - tracker.setRenderRate(refreshRate / 2); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 5 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5 * mPeriod), Eq(mNow + 7 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 9 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 9 * mPeriod), Eq(mNow + 11 * mPeriod)); - - tracker.setRenderRate(refreshRate / 6); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod)); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 7 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + 3 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 3 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 3 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 6 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 6 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod)); } TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { @@ -644,7 +640,7 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { tracker.addVsyncTimestamp(mNow); } - tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod)); + tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod), /*applyImmediately*/ false); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); @@ -655,6 +651,178 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod)); } +TEST_F(VSyncPredictorTest, setRenderRateHighIsAppliedImmediately) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto vsyncRate = Fps::fromPeriodNsecs(500); + const auto minFrameRate = Fps::fromPeriodNsecs(1000); + hal::VrrConfig vrrConfig; + vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs(); + const ftl::NonNull<DisplayModePtr> kMode = + ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID) + .setVrrConfig(std::move(vrrConfig)) + .build()); + + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + + vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false); + vrrTracker.addVsyncTimestamp(0); + EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000)); + + // commit to a vsync in the future + EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); + + vrrTracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ false); + EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000)); + EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); + EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000)); + + EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000)); + + vrrTracker.setRenderRate(Fps::fromPeriodNsecs(3500), /*applyImmediately*/ false); + EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000)); + EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); + EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000)); + EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000)); + EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000)); + EXPECT_EQ(15500, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000)); + EXPECT_EQ(19000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500)); + + vrrTracker.setRenderRate(Fps::fromPeriodNsecs(2500), /*applyImmediately*/ false); + EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000)); + EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); + EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000)); + EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000)); + EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000)); + EXPECT_EQ(15500, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000)); + EXPECT_EQ(19000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500)); + EXPECT_EQ(21500, vrrTracker.nextAnticipatedVSyncTimeFrom(19000, 19000)); + + vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false); + EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000)); + EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); + EXPECT_EQ(7000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000)); + EXPECT_EQ(9000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000)); + EXPECT_EQ(11000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000)); + EXPECT_EQ(13000, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000)); + EXPECT_EQ(17000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500)); + EXPECT_EQ(20000, vrrTracker.nextAnticipatedVSyncTimeFrom(19000, 19000)); +} + +TEST_F(VSyncPredictorTest, minFramePeriodDoesntApplyWhenSameWithRefreshRate) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto vsyncRate = Fps::fromPeriodNsecs(1000); + const auto minFrameRate = Fps::fromPeriodNsecs(1000); + hal::VrrConfig vrrConfig; + vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs(); + const ftl::NonNull<DisplayModePtr> kMode = + ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID) + .setVrrConfig(std::move(vrrConfig)) + .build()); + + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + + vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false); + vrrTracker.addVsyncTimestamp(0); + EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000)); + + // Assume that the last vsync is wrong due to a vsync drift. It shouldn't matter. + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1700)); +} + +TEST_F(VSyncPredictorTest, setRenderRateExplicitAppliedImmediately) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto vsyncRate = Fps::fromPeriodNsecs(500); + const auto minFrameRate = Fps::fromPeriodNsecs(1000); + hal::VrrConfig vrrConfig; + vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs(); + const ftl::NonNull<DisplayModePtr> kMode = + ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID) + .setVrrConfig(std::move(vrrConfig)) + .build()); + + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + + vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false); + vrrTracker.addVsyncTimestamp(0); + EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000)); + + // commit to a vsync in the future + EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 2000)); + + vrrTracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ true); + EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000)); + EXPECT_EQ(7000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); + EXPECT_EQ(9000, vrrTracker.nextAnticipatedVSyncTimeFrom(7000, 7000)); +} + +TEST_F(VSyncPredictorTest, selectsClosestVsyncAfterInactivity) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto vsyncRate = Fps::fromPeriodNsecs(500); + const auto minFrameRate = Fps::fromPeriodNsecs(1000); + hal::VrrConfig vrrConfig; + vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs(); + const ftl::NonNull<DisplayModePtr> kMode = + ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID) + .setVrrConfig(std::move(vrrConfig)) + .build()); + + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + + vrrTracker.setRenderRate(Fps::fromPeriodNsecs(5000), /*applyImmediately*/ false); + vrrTracker.addVsyncTimestamp(0); + EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4700)); + EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); + + mClock->setNow(50000); + EXPECT_EQ(50500, vrrTracker.nextAnticipatedVSyncTimeFrom(50000, 10000)); +} + +TEST_F(VSyncPredictorTest, returnsCorrectVsyncWhenLastIsNot) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto vsyncRate = Fps::fromPeriodNsecs(500); + const auto minFrameRate = Fps::fromPeriodNsecs(1000); + hal::VrrConfig vrrConfig; + vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs(); + const ftl::NonNull<DisplayModePtr> kMode = + ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID) + .setVrrConfig(std::move(vrrConfig)) + .build()); + + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + + vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false); + vrrTracker.addVsyncTimestamp(0); + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1234, 1234)); +} + TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { SET_FLAG_FOR_TEST(flags::vrr_config, true); @@ -670,10 +838,10 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { .setVrrConfig(std::move(vrrConfig)) .build()); - VSyncPredictor vrrTracker{kMode, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent}; + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; - vrrTracker.setRenderRate(minFrameRate); + vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false); vrrTracker.addVsyncTimestamp(0); EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000)); @@ -687,7 +855,95 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { vrrTracker.onFrameMissed(TimePoint::fromNs(4500)); EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500)); EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); + + vrrTracker.onFrameBegin(TimePoint::fromNs(7000), TimePoint::fromNs(6500)); + EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000)); } + +TEST_F(VSyncPredictorTest, adjustsVrrTimelineTwoClients) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto refreshRate = Fps::fromPeriodNsecs(500); + const auto minFrameRate = Fps::fromPeriodNsecs(1000); + hal::VrrConfig vrrConfig; + vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs(); + const ftl::NonNull<DisplayModePtr> kMode = + ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), refreshRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID) + .setVrrConfig(std::move(vrrConfig)) + .build()); + + VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize, + kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + + vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false); + vrrTracker.addVsyncTimestamp(0); + + // App runs ahead + EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700)); + EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3000, 3000)); + EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000)); + + // SF starts to catch up + EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700)); + vrrTracker.onFrameBegin(TimePoint::fromNs(3000), TimePoint::fromNs(0)); + + // SF misses last frame (3000) and observes that when committing (4000) + EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); + EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3700)); + vrrTracker.onFrameMissed(TimePoint::fromNs(4000)); + + // SF wakes up again instead of the (4000) missed frame + EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000)); + vrrTracker.onFrameBegin(TimePoint::fromNs(4500), TimePoint::fromNs(4500)); + + // Timeline shifted. The app needs to get the next frame at (7500) as its last frame (6500) will + // be presented at (7500) + EXPECT_EQ(7500, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000)); + EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500)); + vrrTracker.onFrameBegin(TimePoint::fromNs(5500), TimePoint::fromNs(4500)); + + EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(7500, 7500)); + EXPECT_EQ(6500, vrrTracker.nextAnticipatedVSyncTimeFrom(5500, 5500)); + vrrTracker.onFrameBegin(TimePoint::fromNs(6500), TimePoint::fromNs(5500)); +} + +TEST_F(VSyncPredictorTest, renderRateIsPreservedForCommittedVsyncs) { + tracker.addVsyncTimestamp(1000); + + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000)); + + tracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ false); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(9000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(9000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(11000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(10001), Eq(11000)); + + tracker.setRenderRate(Fps::fromPeriodNsecs(3000), /*applyImmediately*/ false); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(9000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(9000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(11000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(10001), Eq(11000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(11001), Eq(14000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(12001), Eq(14000)); + + // Check the purge logic works + mClock->setNow(20000); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(2000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(8000)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(8000)); +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index 3870983133..c311901c7a 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -29,14 +29,14 @@ public: MOCK_METHOD(bool, addVsyncTimestamp, (nsecs_t), (override)); MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t, std::optional<nsecs_t>), - (const, override)); + (override)); MOCK_METHOD(nsecs_t, currentPeriod, (), (const, override)); MOCK_METHOD(Period, minFramePeriod, (), (const, override)); MOCK_METHOD(void, resetModel, (), (override)); MOCK_METHOD(bool, needsMoreSamples, (), (const, override)); - MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (const, override)); + MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (override)); MOCK_METHOD(void, setDisplayModePtr, (ftl::NonNull<DisplayModePtr>), (override)); - MOCK_METHOD(void, setRenderRate, (Fps), (override)); + MOCK_METHOD(void, setRenderRate, (Fps, bool), (override)); MOCK_METHOD(void, onFrameBegin, (TimePoint, TimePoint), (override)); MOCK_METHOD(void, onFrameMissed, (TimePoint), (override)); MOCK_METHOD(void, dump, (std::string&), (const, override)); |